原文链接:
基于
配置SpringMVC有2个核心类,一个Listener,一个Servlet。
配置示例如下contextConfigLocation classpath*:applicationContext.xml classpath*:applicationContext-*.xml org.springframework.web.context.ContextLoaderListener springServlet org.springframework.web.servlet.DispatcherServlet contextConfigLocation /WEB-INF/spring-mvc.xml 1 springServlet /
由于Listener
总是比Servlet
先加载,所以我们先看ContextLoaderListener
。
ContextLoaderListener
先看ContextLoaderListener的继承结构
发现ContextLoaderListener
实现了Servlet
标准中的ServletContextListener
接口,说明它会监听ServletContext
对象的创建和销毁。所以ContextLoaderListener
一定要实现ServletContextListener
中的2个方法contextInitialized(ServletContextEvent event)
和contextDestroyed(ServletContextEvent event)
。
contextInitialized(ServletContextEvent event)
在ContextLoaderListener中找到
@Overridepublic void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext());}
看到,它先从event
对象中获取到了ServletContext
,然后调用了initWebApplicationContext(ServletContext servletContext)
。
然后找initWebApplicationContext(ServletContext servletContext)
方法,在ContextLoaderListener
中没有找到,那只可能在它的父类中。在ContextLoader
中找到:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. if (this.context == null) { this.context = createWebApplicationContext(servletContext); ---- (1) } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> // determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); ---- (2) cwac.setParent(parent); ---- (3) } configureAndRefreshWebApplicationContext(cwac, servletContext); ---- (4) } } servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ---- (5) ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); ---- (6) } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; }}
核心代码梳理
- (1) 创建
WebApplicationContext
,放到this.context
中; - (2)、(3) 看是否有父容器,并对父容器设置一个引用,即
setParent(ApplicationContext parent)
- (4) 配置
WebApplicationContext
; - (5) 把
this.context
即创建的WebApplicationContext
放到ServletContext
中,key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
; - (6) 把
this.context
即创建的WebApplicationContext
放到currentContextPerThread
中,currentContextPerThread
的类型为private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread = new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1);
小结
从(5)、(6)两行代码的分析可以看出,Spring将初始化好的WebApplicationContext
分别放到了ServletContext
和currentContextPerThread
中。这样可以便于我们在任意代码处,直接获取到WebApplicationContext
。 - 从
ServletContext
中获取WebApplicationContext
,就是在获取到ServletContext
的基础上,根据key(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
)获取到; - 从
currentContextPerThread
获取,Spring为我们提供了一个公有静态方法org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext()
,如下:
public static WebApplicationContext getCurrentWebApplicationContext() { ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl != null) { WebApplicationContext ccpt = currentContextPerThread.get(ccl); if (ccpt != null) { return ccpt; } } return currentContext;}
createWebApplicationContext(ServletContext sc)
先看源码
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);}
基本逻辑是先决定使用哪个ContextClass,然后再进行实例化。
进入determineContextClass(ServletContext servletContext)
方法
protected Class determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); ---- (1) if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); ---- (2) } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); ---- (3) try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); ---- (4) } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } }}
核心代码梳理
- (1) 先尝试从配置文件的初始化参数
CONTEXT_CLASS_PARAM
(即"contextClass")中获取contextClassName
- (2) 如果开发者指定了contextClass,就获取用户指定的这个contextClass的
Class<?>
- (3) 如果开发者没指定,就从默认策略
defaultStrategies
中获取contextClassName
- (4) 最后获取这个默认的
contextClassName
的Class<?>
现在看一下defaultStrategies
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";private static final Properties defaultStrategies;static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. try { ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage()); }}
代码实现了从classpath下读取ContextLoader.properties,然后放入defaultStrategies。再看一下ContextLoader.properties,它位于spring-web-[version].jar的org.springframework.web.context包下,内容是:
# Default WebApplicationContext implementation class for ContextLoader.# Used as fallback when no explicit context implementation has been specified as context-param.# Not meant to be customized by application developers.org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
小结
createWebApplicationContext(ServletContext sc)的逻辑是- 先确定contextClassName。如果开发者通过contextClass参数传入了此值就用此值;如果没传入就用默认的写在ContextLoader.properties中
org.springframework.web.context.support.XmlWebApplicationContext
。 - 得到contextClassName后,获取对应的Class。
- 得到contextClass后,通过反射进行实例化。最终得到了WebApplicationContext对象。
configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc)
先看源码
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); ---- (1) } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); ---- (1) } } wac.setServletContext(sc); ---- (2) String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); ---- (3) if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); ---- (4) } // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); ---- (5) } customizeContext(sc, wac); ---- (6) wac.refresh(); ---- (7)}
核心代码逻辑梳理
- (1) 为
WebApplicationContext
设置了一个id; - (2) 为
WebApplicationContext
建立了一个对ServletContext
的引用。回忆之前的逻辑,发现WebApplicationContext
和ServletContext
是其实相互引用的; - (3) 从初始化参数中获取到配置路径参数,
CONFIG_LOCATION_PARAM
的值为"contextConfigLocation",恰好对应配置文件中的
contextConfigLocation classpath*:applicationContext.xml classpath*:applicationContext-*.xml
说明Spring后续会加载配置文件。
- (4) 为
WebApplicationContext
建立了一个对配置路径参数的引用; - (5) 获取当前环境
ConfigurableEnvironment
,便于后续根据profiles来解析属性; - (6) 对
WebApplicationContext
进行定制,这个方法被定义为protected
,便于子类重写; - (7) 刷新
WebApplicationContext
;
接下来重点看这两行代码,customizeContext(sc, wac)
和wac.refresh()
。
进入customizeContext(sc, wac)
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) { List>> initializerClasses = determineContextInitializerClasses(sc); ---- (1) if (initializerClasses.isEmpty()) { // no ApplicationContextInitializers have been declared -> nothing to do return; } ArrayList > initializerInstances = new ArrayList >(); for (Class > initializerClass : initializerClasses) { Class initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); if (initializerContextClass != null) { Assert.isAssignable(initializerContextClass, wac.getClass(), String.format( "Could not add context initializer [%s] since its generic parameter [%s] " + "is not assignable from the type of application context used by this " + "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(), wac.getClass().getName())); } initializerInstances.add(BeanUtils.instantiateClass(initializerClass)); ---- (2) } AnnotationAwareOrderComparator.sort(initializerInstances); ---- (3) for (ApplicationContextInitializer initializer : initializerInstances) { initializer.initialize(wac); (4) }}
此方法是在WebApplicationContext
设置了配置文件路径之后,在容器刷新之前所可能需要做的一些初始化工作。
- (1) 获取所有上下文初始化的类。determineContextInitializerClasses的源码为
protected List>> determineContextInitializerClasses(ServletContext servletContext) { List >> classes = new ArrayList >>(); String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM); if (globalClassNames != null) { for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) { classes.add(loadInitializerClass(className)); } } String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM); if (localClassNames != null) { for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) { classes.add(loadInitializerClass(className)); } } return classes;}
简单读下源码,就是从2个初始化参数GLOBAL_INITIALIZER_CLASSES_PARAM
(其值为"globalInitializerClasses")和CONTEXT_INITIALIZER_CLASSES_PARAM
(其值为"contextInitializerClasses")中分别解析出ApplicationContextInitializer
的class,放入类型为List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
的集合"classes",然后返回;
- (2) 将所有的
ApplicationContextInitializer
实例化放入initializerInstances
; - (3) 对所有的
initializerInstances
进行排序。排序的规则是:根据类的Order来判断,指定Order有两种方式,一种是实现org.springframework.core.Ordered
接口,另一种是使用@org.springframework.core.annotation.Order
注解指定。具体逻辑参见int org.springframework.core.annotation.AnnotationAwareOrderComparator.getOrder(Object obj)
:
@Overrideprotected int getOrder(Object obj) { if (obj instanceof Ordered) { return ((Ordered) obj).getOrder(); } if (obj != null) { Class clazz = (obj instanceof Class ? (Class ) obj : obj.getClass()); Order order = AnnotationUtils.findAnnotation(clazz, Order.class); if (order != null) { return order.value(); } } return Ordered.LOWEST_PRECEDENCE;}
- (4) 按排好序的
initializerInstances
遍历集合,调用每个ApplicationContextInitializer
的initialize(ConfigurableApplicationContext applicationContext)
方法;
最后看一下ApplicationContextInitializer
接口的定义:
public interface ApplicationContextInitializer{ void initialize(C applicationContext);}
目前Spring并未对此接口提供任何实现,由此说明这个接口只是Spring为我们提供的一个扩展点。在容器刷新前,通过实现此接口,可以对容器做一些操作。接口的实现类可以通过在web.xml中指定globalInitializerClasses或contextInitializerClasses参数,告知Spring。
接下来,我们分析wac.refresh()
。在进入源码之前,回忆一下,前面我们分析了,我们现在获取到的容器是一个定义在ContextLoader.properties中的org.springframework.web.context.support.XmlWebApplicationContext
,先看下XmlWebApplicationContext
的继承结构。 Orz 继承结构超级复杂。
接下来,进入源码
@Overridepublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); ---- (1) // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); ---- (2) // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); ---- (3) try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); ---- (4) // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); ---- (5) // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); ---- (6) // Initialize message source for this context. initMessageSource(); ---- (7) // Initialize event multicaster for this context. initApplicationEventMulticaster(); ---- (8) // Initialize other special beans in specific context subclasses. onRefresh(); ---- (9) // Check for listener beans and register them. registerListeners(); ---- (10) // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); ---- (11) // Last step: publish corresponding event. finishRefresh(); ---- (12) } catch (BeansException ex) { logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex); // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } }}
核心代码逻辑分析
- 首先我们发现这个方法是位于
org.springframework.context.support.AbstractApplicationContext
中,分别又调用了很多方法,我们只挑选一些重要方法详细说明 - (1) 准备刷新。解析属性和校验一些必填项。
- (2) 获取BeanFactory。看下源码
org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory;}
首先它调用了refreshBeanFactory(),方法位于org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()
@Overrideprotected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); ---- [1] beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); ---- [2] loadBeanDefinitions(beanFactory); ---- [3] synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); }}
先判断是否已经有BeanFactory了,如果有就销毁;
然后[1]处通过createBeanFactory()
来创建BeanFactory,返回的是一个DefaultListableBeanFactory
protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory());}
然后给这个DefaultListableBeanFactory
设置一个id; 接下来[2]处是一个以"customize"开头的方法,暗示是子类可以做一些定制化的实现的。 最后[3]处真正的去读取配置文件,加载、注册Bean等工作。具体由org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
实现。具体的过程和使用org.springframework.beans.factory.xml.XmlBeanFactory
差不多,就不细说了。
然后getBeanFactory()
只是做一些对beanFactory是否为空的校验。
- (3) 对BeanFactory做一些准备。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Tell the internal bean factory to use the context's class loader etc. beanFactory.setBeanClassLoader(getClassLoader()); beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver()); beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // Configure the bean factory with context callbacks. beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); beanFactory.ignoreDependencyInterface(EnvironmentAware.class); // BeanFactory interface not registered as resolvable type in a plain factory. // MessageSource registered (and found for autowiring) as a bean. beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // Detect a LoadTimeWeaver and prepare for weaving, if found. if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // Register default environment beans. if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); }}
说几个重要的方法 addBeanPostProcessor(BeanPostProcessor beanPostProcessor)
为bean添加后处理器,每个bean在初始化完成前都会调用注册的BeanPostProcessor
,可以对bean的内容做一些修改; 看下ApplicationContextAwareProcessor
的核心源码,它实现了BeanPostProcessor
接口。
@Overridepublic Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException { AccessControlContext acc = null; if (System.getSecurityManager() != null && (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); } if (acc != null) { AccessController.doPrivileged(new PrivilegedAction
看到每个bean初始化完成后,都会判断它是不是实现了Aware类的接口,然后会调用invokeAwareInterfaces(Object bean)
方法,来注入相应的Aware接口对应的对象。
ignoreDependencyInterface(Class<?> ifc)
忽略依赖接口。这里看到忽略的都是一些Aware接口;registerResolvableDependency(Class<?> dependencyType, Object autowiredValue)
如果代码中需要依赖注入dependencyType
类型的变量,就会被自动装配入autowiredValue
的值;registerSingleton(String beanName, Object singletonObject)
向容器中注册单例; - (4) 对BeanFactory进行后处理。在
AbstractApplicationContext
中定义的是一个空方法,交给子类org.springframework.web.context.support.AbstractRefreshableWebApplicationContext.postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
处理
@Overrideprotected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig)); beanFactory.ignoreDependencyInterface(ServletContextAware.class); beanFactory.ignoreDependencyInterface(ServletConfigAware.class); WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext); WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);}
又对beanFactory添加了一个后处理器。与前面的ApplicationContextAwareProcessor
类似,我们打开ServletContextAwareProcessor
的源码,我们看到它实现了BeanPostProcessor
接口
@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (getServletContext() != null && bean instanceof ServletContextAware) { ((ServletContextAware) bean).setServletContext(getServletContext()); } if (getServletConfig() != null && bean instanceof ServletConfigAware) { ((ServletConfigAware) bean).setServletConfig(getServletConfig()); } return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) { return bean;}
发现它就是去判断:如果Bean实现了ServletContextAware
接口,就设置对ServletContext
的引用;如果实现了ServletConfigAware
接口,就设置对ServletConfig
对象的引用;
然后忽略了这两个Aware接口。
接下来,注册WebAppliction的Scope。看源码
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) { beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope()); beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false)); beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true)); if (sc != null) { ServletContextScope appScope = new ServletContextScope(sc); beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope); // Register as ServletContext attribute, for ContextCleanupListener to detect it. sc.setAttribute(ServletContextScope.class.getName(), appScope); } beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory()); beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); if (jsfPresent) { FacesDependencyRegistrar.registerFacesDependencies(beanFactory); }}
前3行注册了3个Scope,分别是request、session、global_session。然后通过ServletContextScope
包装了一个应用级别的scope,在注册scope后,还放入了ServletContext中。
registerResolvableDependency(Class<?> dependencyType, Object autowiredValue)
,如果注入dependencyType
类型的变量,就会被自动装配入autowiredValue
的值。但注意,这里3个都是ObjectFactory
。ObjectFactory
对获取对象的值又做了一个包装,真正注入的值是T ObjectFactory.getObject()
。这种方式与FactoryBean<T>
的机制有点像。这里只看一下RequestObjectFactory
的源码 @SuppressWarnings("serial")private static class RequestObjectFactory implements ObjectFactory, Serializable { @Override public ServletRequest getObject() { return currentRequestAttributes().getRequest(); } @Override public String toString() { return "Current HttpServletRequest"; }}
也就是说,如果我们代码中注入的值是ServletRequest
类型的,其值是currentRequestAttributes().getRequest()
的返回值。
另外补充一点,上面说会对ObjectFactory
类型的自动装配值有特殊处理,这个特殊处理在哪呢?我们打开registerResolvableDependency(Class<?> dependencyType, Object autowiredValue)
this.resolvableDependencies.put(dependencyType, autowiredValue);
看到所有可解析的依赖都放到了变量resolvableDependencies
中,它的类型是Map<Class<?>, Object>
,那我们看它在什么时候执行了get()
呢?
findAutowireCandidates(String beanName, Class<?> requiredType, DependencyDescriptor descriptor)
方法中,核心代码是 Object autowiringValue = this.resolvableDependencies.get(autowiringType);autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
再进入AutowireUtils.resolveAutowiringValue(Object autowiringValue, Class<?> requiredType)
public static Object resolveAutowiringValue(Object autowiringValue, Class requiredType) { if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) { ObjectFactory factory = (ObjectFactory ) autowiringValue; if (autowiringValue instanceof Serializable && requiredType.isInterface()) { autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(), new Class [] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory)); } else { return factory.getObject(); } } return autowiringValue;}
看到对ObjectFactory
的特殊处理了吧?但因为ServletRequest
,包括HttpServletRequest
都是接口,所以这里是会通过JDK的动态代理生成一个代理类。具体调用时的逻辑,请看ObjectFactoryDelegatingInvocationHandler
的逻辑
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable { private final ObjectFactory objectFactory; public ObjectFactoryDelegatingInvocationHandler(ObjectFactory objectFactory) { this.objectFactory = objectFactory; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (methodName.equals("equals")) { // Only consider equal when proxies are identical. return (proxy == args[0]); } else if (methodName.equals("hashCode")) { // Use hashCode of proxy. return System.identityHashCode(proxy); } else if (methodName.equals("toString")) { return this.objectFactory.toString(); } try { return method.invoke(this.objectFactory.getObject(), args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } }}
不再详述了,看不懂的,去补一下JDK动态代理的知识。
最后,注册环境相关的Bean。这个就不细说了..
- (5) 调用BeanFactory级的后处理器。
- (6) 注册所有的BeanPostProcessor。
- (7) 初始化一些国际化文件。
- (8) 初始化应用事件组播器。源码
protected void initApplicationEventMulticaster() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); if (logger.isDebugEnabled()) { logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]"); } } else { this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); if (logger.isDebugEnabled()) { logger.debug("Unable to locate ApplicationEventMulticaster with name '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "': using default [" + this.applicationEventMulticaster + "]"); } }}
如果beanFactory中有,会从beanFactory中取;如果没有就用SimpleApplicationEventMulticaster
,并将其以单例bean的方式注册在容器中。
- (9) 刷新容器。只是简单的初始化了一些主题(Theme)。
- (10) 注册应用监听器。第(8)步已经初始化了事件组播器,这里将监听器都注册好,等待事件分发。
- (11) 完成beanFactory初始化。对所有非抽象非懒加载的bean,都执行一下getBean()方法,使bean实例缓存。见
finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)
的最后一行beanFactory.preInstantiateSingletons();
,就不贴源码了。 - (12) 完成刷新。看源码
protected void finishRefresh() { // Initialize lifecycle processor for this context. initLifecycleProcessor(); // Propagate refresh to lifecycle processor first. getLifecycleProcessor().onRefresh(); // Publish the final event. publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. LiveBeansView.registerApplicationContext(this);}
重点看发布事件这一行,首先构造了一个ContextRefreshedEvent
事件,事件源传入的是this
,也就是当前的Spring容器。
publishEvent(ApplicationEvent event)
@Overridepublic void publishEvent(ApplicationEvent event) { Assert.notNull(event, "Event must not be null"); if (logger.isTraceEnabled()) { logger.trace("Publishing event in " + getDisplayName() + ": " + event); } getApplicationEventMulticaster().multicastEvent(event); if (this.parent != null) { this.parent.publishEvent(event); }}
代码很简单,利用应用组播器广播事件,如果有父容器,也向父容器中广播。事件对象中还封装了已完全初始化好的Spring容器作为其事件源。此时所有的监听了ContextRefreshedEvent
事件的ApplicationListener
不仅可以触发事件通知,还能从事件对象中获取到完全初始化好的Spring容器。
Event
相关类的包装。 小结 由此可见,可以通过定义监听ContextRefreshedEvent
事件的ApplicationListener
,来在已完全初始化好Spring容器后,立刻获取到通知事件,并可以在事件中获取到相应的Spring容器对象。
- 定义一个ApplicationListener
package personal.wanghui.quickstart.event;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationListener;import org.springframework.context.event.ContextRefreshedEvent;public class MyApplicationListener implements ApplicationListener{ private static final Logger logger = LoggerFactory.getLogger(MyApplicationListener.class); @Override public void onApplicationEvent(ContextRefreshedEvent event) { ApplicationContext applicationContext = event.getApplicationContext(); logger.info("{}", applicationContext); }}
- 将
MyApplicationListener
配置到Spring的配置文件中
- 启动应用,打印出日志: 2016-07-06 17:30:09,687 INFO personal.wanghui.quickstart.event.MyApplicationListener:20 - Root WebApplicationContext: startup date [Wed Jul 06 17:29:53 CST 2016]; root of context hierarchy 2016-07-06 17:30:15,531 INFO personal.wanghui.quickstart.event.MyApplicationListener:20 - WebApplicationContext for namespace 'springServlet-servlet': startup date [Wed Jul 06 17:30:09 CST 2016]; parent: Root WebApplicationContext
如果项目中有SpringMVC会打印出两句;如果没有,则只会打印出第一句;如果将此myApplicationListener
配置到SpringMVC的配置文件中,则只会打印出第二句。这是为什么呢?此问题我们在后续分析SpringMVC的容器时做出解答。
现在还有一个问题,为什么只要把myApplicationListener
声明为Spring Bean,就可以被注册为监听器呢?Spring是在哪里注册的呢?
org.springframework.context.support.AbstractApplicationContext.registerListeners()
,前面讲过这个方法的作用,但没有具体看源码。 protected void registerListeners() { // Register statically specified listeners first. for (ApplicationListener listener : getApplicationListeners()) { ---- (1) getApplicationEventMulticaster().addApplicationListener(listener); ---- (2) } // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let post-processors apply to them! String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); ---- (3) for (String lisName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(lisName); ---- (4) }}
第(2)和(4)代码分别都是向应用事件组播器添加应用监听器,只不过方式不同。
(1)处是从容器自己添加的应用监听器中获取所有的应用监听器,再添加给事件组播器; (3)处是从容器中根据类型找到所有实现了ApplicationListener
接口的bean,再添加给事件组播器;显然我们自己在xml配置文件中配置的myApplicationListener
属于此种情况,因此它可以得到正确的事件通知。 至此,Web环境下Spring容器的初始化过程就分析到这里。
contextDestroyed(ServletContextEvent event)
Web应用服务器关闭时,会调用所有Listener
的contextDestroyed(ServletContextEvent event)
方法,接下来看下ContextLoaderListener
中的此方法。
@Overridepublic void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext());}
代码很简单,先是关闭容器,然后是清理属性。 进入org.springframework.web.context.ContextLoader.closeWebApplicationContext(ServletContext servletContext)
public void closeWebApplicationContext(ServletContext servletContext) { servletContext.log("Closing Spring root WebApplicationContext"); try { if (this.context instanceof ConfigurableWebApplicationContext) { ((ConfigurableWebApplicationContext) this.context).close(); ---- (1) } } finally { ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = null; } else if (ccl != null) { currentContextPerThread.remove(ccl); ---- (2) } servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); ---- (3) if (this.parentContextRef != null) { this.parentContextRef.release(); } }}
核心代码逻辑梳理
- (1) 直接调用容器的
close()
方法 - 还记得之前分析的吗?实例化完的容器放到了2个地方,一个是
ServletContext
中,一个是Map<ClassLoader, WebApplicationContext> currentContextPerThread
中,(2)和(3)就是分别清理掉这2个地方的容器引用。
然后看一下org.springframework.web.context.ContextCleanupListener.cleanupAttributes(ServletContext sc)
static void cleanupAttributes(ServletContext sc) { EnumerationattrNames = sc.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = attrNames.nextElement(); if (attrName.startsWith("org.springframework.")) { Object attrValue = sc.getAttribute(attrName); if (attrValue instanceof DisposableBean) { try { ((DisposableBean) attrValue).destroy(); } catch (Throwable ex) { logger.error("Couldn't invoke destroy method of attribute with name '" + attrName + "'", ex); } } } }}
代码的主要功能是从ServletContext中取出所有key以org.springframework.
开头的对象,如果这些对象实现了DisposableBean
接口,就调用其destroy()
方法。
至此,通过ContextLoaderListener初始化Spring容器的全部过程就梳理完了。