原文链接: http://my.oschina.net/ojeta/blog/716549
基于SpringFramework 4.0.9 版本
配置SpringMVC有2个核心类,一个Listener,一个Servlet。
配置示例如下
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:applicationContext.xml
classpath*:applicationContext-*.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener </listener-class>
</listener>
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-mvc.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
由于Listener
总是比Servlet
先加载,所以我们先看ContextLoaderListener
。
ContextLoaderListener
先看ContextLoaderListener的继承结构
发现ContextLoaderListener
实现了Servlet
标准中的ServletContextListener
接口,说明它会监听ServletContext
对象的创建和销毁。所以ContextLoaderListener
一定要实现ServletContextListener
中的2个方法contextInitialized(ServletContextEvent event)
和contextDestroyed(ServletContextEvent event)
。
contextInitialized(ServletContextEvent event)
在ContextLoaderListener中找到
@Override
public 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",恰好对应配置文件中的
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:applicationContext.xml
classpath*:applicationContext-*.xml
</param-value>
</context-param>
说明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<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc); ---- (1)
if (initializerClasses.isEmpty()) {
// no ApplicationContextInitializers have been declared -> nothing to do
return;
}
ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> 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<ConfigurableApplicationContext> initializer : initializerInstances) {
initializer.initialize(wac); (4)
}
}
此方法是在WebApplicationContext
设置了配置文件路径之后,在容器刷新之前所可能需要做的一些初始化工作。
- (1) 获取所有上下文初始化的类。determineContextInitializerClasses的源码为
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
determineContextInitializerClasses(ServletContext servletContext) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
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)
:
@Override
protected 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<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
目前Spring并未对此接口提供任何实现,由此说明这个接口只是Spring为我们提供的一个扩展点。在容器刷新前,通过实现此接口,可以对容器做一些操作。接口的实现类可以通过在web.xml中指定globalInitializerClasses或contextInitializerClasses参数,告知Spring。
接下来,我们分析wac.refresh()
。在进入源码之前,回忆一下,前面我们分析了,我们现在获取到的容器是一个定义在ContextLoader.properties中的org.springframework.web.context.support.XmlWebApplicationContext
,先看下XmlWebApplicationContext
的继承结构。 Orz 继承结构超级复杂。
接下来,进入源码
@Override
public 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()
@Override
protected 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
接口。
@Override
public 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<Object>() {
@Override
public Object run() {
invokeAwareInterfaces(bean);
return null;
}
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
看到每个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)
处理
@Override
protected 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
接口
@Override
public 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;
}
@Override
public 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<ServletRequest>, 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)
@Override
public 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容器。
这里的事件机制,其实是对jdk中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<ContextRefreshedEvent> {
private static final Logger logger = LoggerFactory.getLogger(MyApplicationListener.class);
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
logger.info("{}", applicationContext);
}
}
- 将
MyApplicationListener
配置到Spring的配置文件中
<bean id="myApplicationListener" class="personal.wanghui.quickstart.event.MyApplicationListener" />
- 启动应用,打印出日志:
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
中的此方法。
@Override
public 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) {
Enumeration<String> attrNames = 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容器的全部过程就梳理完了。