众所周知,Spring有2大核心,IOC和AOP,而IOC又是AOP的基础,因此Spring源码学习系列的第一篇博客以IOC开始。本篇博客以IOC容器的启动流程为切入点,介绍从web容器启动,Spring ContextLoaderListener监听到ServletContextEvent并初始化WebApplicationContext到最终WebApplicationContext初始化完毕存入ServletContext的大致流程。
Spring容器有多种启动方式,这里以常见web.xml配置为例,介绍web容器是如何启动Spring容器的。
这里干了2件事:
1、配置了一个监听器ContextLoaderListener。
2、设置参数contextConfigLocation,值为类路径下所有applicationContext*.xml文件。
接下来我们看下这个ContextLoaderListener是怎么工作的。
这是一个容器加载监听器,继承了ContextLoader并实现了ServletContextListener接口,可以监听到容器初始化以及销毁事件。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
/**
* 开始初始化WebApplicationContext
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
/**
* 关闭容器
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
当web容器启动后被contextInitialized(ServletContextEvent event) 监听到,进而执行ContextLoader#initWebApplicationContext(event.getServletContext()) 方法。
看名字就知道这是一个上下文加载器,容器的实际初始化工作是从这里开始的。我们看下initWebApplicationContext方法实现
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
/**
* 检查servletContext中是否已经存放有1个root application,如果有,那么抛出IllegalStateException。
* 比如在web.xml中配置多个ContextLoader,导致启动ServletContext时初始化了多个WebApplicationContext。
* 正常情况下,当WebApplicationContext初始化完毕后会作为属性存储在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!");
}
servletContext.log("Initializing Spring root WebApplicationContext");
Log logger = LogFactory.getLog(ContextLoader.class);
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) {
/**
* 使用无参构造(Java类)或者主要构造(Kotlin类)实例化1个ConfigurableWebApplicationContext实现类,
* 默认是XMLWebApplicationContext。
* ps:为什么默认是XMLWebApplicationContext?
* 参见:ContextLoader的static代码块,初始化defaultStrategies,会加载ContextLoader.properties
* org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
*
* Spring中大量用到了了defaultStrategies,定义在1个properties文件中,然后在static代码块加载这个配置文件,缓存里面的配置。
* 比如DispatcherServlet会使用DispatcherServlet.properties作为默认策略,该配置文件集中定义了Spring Mvc中的默认组件,如
* LocaleResolver、ThemeResolver、HandlerMapping、HandlerAdapter、HandlerExceptionResolver、ViewResolver等
*/
this.context = createWebApplicationContext(servletContext);
}
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.
/**
* 获取父容器并进行设置,loadParentContext(ServletContext servletContext)是个模板方法,
* 默认返回null,如果需要定制可以使用子类继承并进行复写
*/
ApplicationContext parent = loadParentContext(servletContext);
/**
* parent默认是null
*/
cwac.setParent(parent);
}
/**
* 最最重要的步骤:配置并刷新WebApplicationContext!!!
*/
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
/**
* WebApplicationContext初始化完成,存入当前servletContext,非常重要,正是因为这一点,可以通过下面方式获取WebApplicationContext
* 1、WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc)
* 2、WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)
* 区别:1获取失败抛出异常,2返回null
*/
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
/**
* 将初始化完成的WebApplicationContext存入当前ContextLoader,可通过下面方式获取WebApplicationContext
* ContextLoader.getCurrentWebApplicationContext()
*/
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}
总的来说,就是干了下面几件事:
1、初始化之前检查当前ServletContext是否已经存在Spring容器;
2、使用无参构造(Java类)或者主要构造(Kotlin类)实例化1个ConfigurableWebApplicationContext实现类,默认是XMLWebApplicationContext;
3、为已经实例化的ConfigurableWebApplicationContext设置父容器,通常为null,除非自定义子类复写loadParentContext。
4、配置并刷新ConfigurableWebApplicationContext(重点);
5、以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为key存储在当前ServletContext中;
6、将ConfigurableWebApplicationContext存储在当前ContextLoader内部。
这里面最重要的方法是configureAndRefreshWebApplicationContext(cwac, servletContext) ,负责为ConfigurableWebApplicationContext设置一些属性为接下来的refresh做准备
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
/**
* 给ConfigurableApplicationContext实现类设置1个unique id
*/
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);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
/**
* 为当前ConfigurableApplicationContext设置ServletContext
*/
wac.setServletContext(sc);
/**
* 获取web.xml中的contextConfigLocation配置信息,通常是spring的xml配置文件地址
*/
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
/**
* 为当前ConfigurableApplicationContext设置contextConfigLocation配置信息(xml配置文件)
*/
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
/**
* 初始化PropertySources,一般只要容器调用refresh()方法,就会初始化PropertySources。
* 这个地方之所以现在就初始化视为了供refresh()方法执行前的 1、后处理 2、初始化 操作使用
*/
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
/**
* 自定义容器配置,在为容器设置了ConfigLocation之后,refresh之前执行
*/
customizeContext(sc, wac);
/**
* 容器刷新,加载配置好的XML、properties或者relational database schema文件
* 另外由于这个是容器启动方法,有1点需要注意:如果容器启动失败,所有已创建好的singletons将被销毁。
* 此方法有多个实现,但是主体部分是AbstractApplicationContext的refresh()
*/
wac.refresh();
}
可以看到先是为ConfigurableWebApplicationContext设置了一些属性,然后开始refresh容器
1、设置ConfigurableWebApplicationContext的unique id;
2、调用wac.setServletContext(sc),设置ServletContext为当前ServletContext;
3、获取web.xml中contextConfigLocation配置信息,并调用wac.setConfigLocation(configLocationParam)进行设置(重要)
4、提前初始化PropertySources以便在refresh前执行一些后处理或者初始化工作。
5、自定义一些容器配置,设置了ConfigLocation之后,refresh之前执行
6、上面工作完成后,开始refresh容器(很重要!很重要!很重要!)
属性设置完成,要刷新容器了,我们进入AbstractApplicationContext的refresh() 来一探究竟!
首先看下refresh()方法实现,这是Spring容器初始化最核心的方法,前面都只是准备工作,不管以哪种方式启动Spring容器,最终必定会调用这个方法。限于篇幅原因,本篇博客只是大致介绍refresh方法各个流程,让大家先有个印象,具体每个流程的细节将会在后续博客陆续阐述。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
/**
* 刷新前的准备工作:
* 1、设置启动日期
* 2、激活flag,如closed=false,active=true
* 3、初始化PropertySources(处理配置文件中的占位符)并校验必须参数(通过ConfigurablePropertyResolver#setRequiredProperties设置)
*/
prepareRefresh();
/**
* 刷新并返回1个BeanFactory,概括起来干了下面几件事:
* 1、判断是否已经有了BeanFactory,如果有了就销毁所单例bean,并关闭BeanFactory。
* 2、初始化1个BeanFactory,设置允许BeanDefinition覆盖以及循环引用
* 3、根据之前设置好的ConfigLocation定位配置文件,加载并解析形成beanName-beanDefinition形式的map并存储在
* BeanFactory中。
* ps:在ContextLoader中wac.setConfigLocation(configLocationParam);
* 接口方法在ConfigurableWebApplicationContext中setConfigLocation(String configLocation)
* 4、将设置好的beanFactory存入ApplicationContext内部
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* 为beanFactory做些准备工作
* 1、设置beanFactory的ClassLoader
* 2、添加3个BeanPostProcessor
* (1)ApplicationContextAwareProcessor---在bean初始化前回调bean实现的相关Aware接口方法进行依赖注入
* (2)ApplicationListenerDetector---在bean初始化后检查bean如果实现ApplicationListener接口,就注册为ApplicationListener存入set中
* (3)LoadTimeWeaverAwareProcessor---AspectJ相关,在bean初始化后检查bean如果实现LoadTimeWeaverAware接口,就调用setLoadTimeWeaver进行注入
* 3、忽略和注册bean的一些特殊依赖
* (1)忽略一些Aware接口实现类依赖
* (EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware
* 、MessageSourceAware、ApplicationContextAware)
* (2)注册当前ApplicationContext本身或者内部beanFactory作为某些特殊依赖
* (BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext)
* 4、对于一些特殊bean(environment、systemProperties、systemEnvironment),如果用户没有注册,那么Spring将自动注册
*/
prepareBeanFactory(beanFactory);
try {
/**
* 第一个扩展点:postProcessBeanFactory(beanFactory)是一个模板方法,本身并没有实现。
* 前面的工作已经添加了一些通用的BeanPostProcessor
* 用户如果继承ApplicationContext实现自己的子类,那么可以在这个地方添加一些自已感兴趣的BeanPostProcessor
*/
postProcessBeanFactory(beanFactory);
/**
* 执行所有BeanFactoryPostProcessor实现类的回调方法,必须在
* 任何单例bean实例化之前执行
*/
invokeBeanFactoryPostProcessors(beanFactory);
/**
* 注册前面添加的所有BeanPostProcessor实现类,必须在所有容器bean实例化之前注册
* 注意:这里只是注册,还没有执行回调方法,这些回调方法会在bean初始化前后执行
*/
registerBeanPostProcessors(beanFactory);
/**
* 初始化当前容器的MessageSource,国际化相关,非重点
*/
initMessageSource();
/**
* 初始化当前容器的ApplicationEventMulticaster
*/
initApplicationEventMulticaster();
/**
* 第二个扩展点:onRefresh()是1个模板方法。在context子类中初始化一些其他特殊的bean(在单例bean初始化之前执行)
*/
onRefresh();
/**
* 注册ApplicationListener实现类,即事件监听器
*/
registerListeners();
/**
* 实例化所有非懒加载的单例bean
* 在上面的ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()中,
* 只是注册了beanDefinition,还没有真正实例化,在这里会进行真正的实例化
*/
finishBeanFactoryInitialization(beanFactory);
/**
* 到这里,容器已经初始化完毕,会发布ContextRefreshedEvent。
* 传统的容器启动后执行初始化方法有3种方式:
* 1、在bean标签中指定init-method,或者@Bean(initMethod=???)
* 2、实现InitializingBean接口
* 3、使用@PostConstruct
*
* 这里我们可以利用这个事件实现第4种方式
* 比如实现ApplicationListener接口,当监听到ContextRefreshedEvent事件时,方法onApplicationEvent(event)
* 将被调用,然后做一些初始化工作!
*/
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
/**
* 如果容器刷新异常,销毁所有已经创建的单例bean
*/
destroyBeans();
/**
* 设置active=false,表示容器没有刷新过,允许下一次刷新
*/
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
/**
* 重置缓存数据
*/
resetCommonCaches();
}
}
}
上面的代码片段和注释描述了容器refresh的所有流程,下面简单看下这些流程
这个流程主要是刷新前的准备工作
protected void prepareRefresh() {
// Switch to active.
this.startupDate = System.currentTimeMillis();
//设置容器开启
this.closed.set(false);
//设置容器已激活,如果容器初始化异常会重置active为false,表示未激活,下次可以重新refresh
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
//模板方法,初始化PropertySource处理配置文件中的占位符
initPropertySources();
//校验设置为required的参数,在ConfigurablePropertyResolver#setRequiredProperties中设置
getEnvironment().validateRequiredProperties();
//初始化earlyApplicationListeners存储预刷新的ApplicationListener
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
//初始化earlyApplicationEvents存储早期ApplicationEvent,一旦multicaster可用便会发布这些事件
this.earlyApplicationEvents = new LinkedHashSet<>();
}
这个方法很重要,内部有1个模板方法refreshBeanFactory(),负责初始化1个DefaultListableBeanFactory,并维护在ApplicationContext内部,以后ApplicationContext有关BeanFactory的工作都由这个DefaultListableBeanFactory去完成!
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
/**
* 刷新BeanFactory,这是1个模板方法,需要子类去实现(Spring用到了大量模板+钩子设计模式)
* 此处我们以AbstractRefreshableApplicationContext实现为例
*/
refreshBeanFactory();
//返回刷新好的BeanFactory
return getBeanFactory();
}
refreshBeanFactory()是一个模板方法,由子类负责实现。这里我们以AbstractRefreshableApplicationContext为例介绍下这个实现。
@Override
protected final void refreshBeanFactory() throws BeansException {
/**
* 判断是否已经有了BeanFactory
*/
if (hasBeanFactory()) {
/**
* 如果已经有了:
* 1、销毁所有单例bean
* 2、关闭BeanFactory
*/
destroyBeans();
closeBeanFactory();
}
try {
/**
* 创建1个DefaultListableBeanFactory
*/
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
/**
* 设置2个属性:非常重要,尤其是第2个属性
* 1、allowBeanDefinitionOverriding:是否允许BeanDefinition覆盖(默认true,即对于name相同的bean,后面的将覆盖前面的)
* 2、allowCircularReferences:是否允许循环引用(默认true)
*/
customizeBeanFactory(beanFactory);
/**
* 加载beanDefinition到beanFactory:注意是beanDefinition(bean的原始数据),不是bean,此时bean尚未实例化
* 此方法有多个实现,对于XmlWebApplicationContext来说,就是加载web.xml中contextConfigLocation配置的spring xml配置文件
* 如spring-mvc.xml,spring-jdbc.xml,applicationContext.xml等,解析完成后以beanName-beanDefinition的k-v形式缓存在
* beanFactory中
*/
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
/**很重要:
* 将设置好的beanFactory存入ApplicationContext内部,
* 以后ApplicationContext中与beanFactory相关的操作都由这个beanFactory来完成
*/
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
有2个细节需要特别注意:
1、customizeBeanFactory(beanFactory),设置2个重要属性,尤其是第2个
(1)allowBeanDefinitionOverriding:是否允许BeanDefinition覆盖(默认true,即对于name相同的bean,后面的将覆盖前面的)
(2)allowCircularReferences:是否允许循环引用(默认true)
2、loadBeanDefinitions(beanFactory),加载配置文件并解析,注册BeanDefinition到BeanFactory中,以便后面实例化bean。
在3.2 中已经初始化了1个BeanFactory并维护在ApplicationContext内部,在设置了2个重要属性和加载配置文件、解析并注册BeanDefinition后,这一步需要继续为这个BeanFactory做些准备工作,主要是以下内容:
1、设置beanFactory的ClassLoader为当前当前ApplicationContext的类加载器
2、注册3个BeanPostProcessor
(1)ApplicationContextAwareProcessor—在bean初始化前回调bean实现的相关Aware接口方法进行依赖注入
(2)ApplicationListenerDetector—在bean初始化后检查bean如果实现ApplicationListener接口,就注册为ApplicationListener存入set中
(3)LoadTimeWeaverAwareProcessor—AspectJ相关,在bean初始化后检查bean如果实现LoadTimeWeaverAware接口,就调用setLoadTimeWeaver进行注入
3、忽略和注册bean的一些特殊依赖
(1)忽略一些Aware接口实现类依赖
(EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware)
(2)注册当前ApplicationContext本身或者内部beanFactory作为某些特殊依赖
(BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext)
4、自动注册一些特殊bean:对于一些特殊bean(environment、systemProperties、systemEnvironment),如果用户没有注册,那么Spring将自动注册
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
/**
* 设置内部beanFactory的ClassLoader为加载当前ApplicationContext的类加载器
*/
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
/**
* 注册1个BeanPostProcessor---ApplicationContextAwareProcessor,极为重要的1个BeanPostProcessor,会在bean
* 初始化之前检查bean是否实现了某些Aware接口并回调相应的set方法进行装配
*/
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
/**
* 如果bean依赖于以下接口的实现类,那么在自动装配时将忽略掉
* ps:那Spring通过什么方式进行装配?
* Spring会通过上面注册的ApplicationContextAwareProcessor进行装配,该接口的postProcessBeforeInitialization方法会在
* 每一个bean初始化之前依次检查bean是否实现下面接口:
* 1、EnvironmentAware
* 2、EmbeddedValueResolverAware
* 3、ResourceLoaderAware
* 4、ApplicationEventPublisherAware
* 5、MessageSourceAware
* 6、ApplicationContextAware
* 如果实现了将调用相应的set方法进行设置!
*/
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
/**
* 如果bean依赖了下面几个接口的实现类,那么注入相应的值:
* 1、由于ApplicationContext本身继承了ResourceLoader,ApplicationEventPublisher,ApplicationContext,因此直接注入当前
* ApplicationContext即可
* 2、由于ApplicationContext内部维护了1个初始化完成的BeanFactory,因此直接注入ApplicationContext内部beanFactory
* 3、之所以没有MessageSource,是因为MessageSource被注册成为1个普通的bean
*/
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
/**
* 注册BeanPostProcessor---ApplicationListenerDetector,用于在bean初始化完成后检查该bean
* 是否实现ApplicationListener接口,如果是,那么将其注册为ApplicationListener(事件监听器)
* AbstractApplicationContext内部维护了1个Set>
*/
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
/**
* 如果注册了名为loadTimeWeaver的bean到beanFactory中,
* 那么注册BeanPostProcessor---LoadTimeWeaverAwareProcessor。
* 这个bean很特殊,涉及到AspectJ,在运行期内进行织入,与Spring AOP 不一样。
*
* LoadTimeWeaverAwareProcessor会在bean初始化完成后检查bean是否
* 实现了LoadTimeWeaverAware接口,如果是,从beanFactory中获取loadTimeWeaver,
* 并调用bean.setLoadTimeWeaver(loadTimeWeaver)进行注入
*
*/
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.
/**
* 如果beanFactory未注册environment,此处自动注册1个
*/
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
/**
* 如果beanFactory未注册systemProperties,此处自动注册1个
*/
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
/**
* 如果beanFactory未注册systemEnvironment,此处自动注册1个
*/
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
在3.3中,Spring为我们自动注册了几个BeanPostProcessor,如果还是无法满足我们的需求,那么我们可以在这一步自己手动注册一些感兴趣的BeanPostProcessor!
/**
* 在ConfigurableListableBeanFactory初始化完成后做一些改动(此时所有definition都已注册但尚未实例化)。
* 我们可以在自定义ApplicationContext实现类中重写这个方法并添加一些特殊的BeanPostProcessor
* @param beanFactory
*/
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
Spring为这个模板方法提供了几个实现,我们以AbstractRefreshableWebApplicationContext#postProcessBeanFactory为例看看这个实现
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
/**
* 注册一个BeanPostProcessor---ServletContextAwareProcessor,用于bean初始化之前
* 执行回调方法检查bean是否实现ServletContextAware和ServletConfigAware接口,
* 如果是,那么注入依赖ServletContext和ServletConfig
*/
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
/**
* 上面已经处理了这2种依赖,下面直接忽略掉
*/
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
/**
* 非常重要:注册web容器需要用到的相关作用域bean:
* 1、request
* 2、session
* 3、globalSession
* 4、application
*/
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
/**
* 注册环境相关bean
*/
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
这里面有个细节非常重要:
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext)
这行代码用于注册request、session、globalSession、application这4种web容器相关作用域bean。
有什么用呢?我们可以在Controller中以下面这种方式注入每次请求的request、response以及session
@RestController
public class ProjectController {
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
@Autowired
private HttpSession session;
}
此处注入的request、response、session将绑定每次请求的真实request、response、session,而且线程安全!!!
至于为什么可以这么用,这个涉及到Spring MVC中DispatcherServlet以及IOC容器依赖装配相关内容,又是一个很大的领域,将在本系列后续博客中阐述。
在1个bean的生命周期中需要经过多步操作才能最终使用,其中第一步就是执行BeanFactoryPostProcessor的回调方法
postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
/**
* 调用所有注册的BeanFactoryPostProcessor实现类的postProcessBeanFactory(factory) 回调方法
*/
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
/**
* 再次检查beanFactory是否注册了ltw,如果是那就注册BeanPostProcessor---LoadTimeWeaverAwareProcessor
*/
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
在前面的工作中,我们已经添加了所有的BeanPostProcessor(Spring自动添加,如果有必要,你也可以手动添加一些感兴趣的BeanPostProcessor),在这一步中,需要将这些BeanPostProcessor注册到beanFactory中,注意仅仅是注册,还没有调用里面的回调方法(回调方法会在后面bean初始化前后执行)
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
初始化当前容器的MessageSource,国际化相关,非重点,直接跳过
初始化当前容器的ApplicationEventMulticaster,事件广播器,跳过
又是一个扩展点,模板方法,在context子类中初始化一些其他特殊的bean(在单例bean初始化之前执行),不是我们关注的重点,跳过
注册实现了ApplicationListener接口的bean为监听器,用于监听容器事件,不展开说。
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// 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);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
终于来到了最重要的部分,这一步是refresh方法最重要的一步,也是最难最复杂的部分,用于实例化所有非懒加载的单例bean。
在上面的ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()中,
只是注册了beanDefinition,还没有真正实例化,在这里会进行真正的实例化,经过这一步,bean才算是真正可以使用了。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
/**
* Spring 3.0添加的属性转换服务,作为JavaBeans PropertyEditors的替代方案
* 用于某些bean属性进行转换是使用,比较重要的一个点
*/
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
//调用getBean提前初始化
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
//停止使用临时类加载器
beanFactory.setTempClassLoader(null);
//因为下一步就要与初始化所有非懒加载单例bean了,在这里冻结配置信息,即将所有definition元数据缓存起来,以避免被修改或者有任何后处理操作
beanFactory.freezeConfiguration();
//实例化所有剩余的非懒加载单例bean
beanFactory.preInstantiateSingletons();
}
接下来就是beanFactory.preInstantiateSingletons()了,这里不继续展开,后续将分多篇博客来介绍里面的内容。
到这里,容器已经初始化完毕,会发布ContextRefreshedEvent。
传统的容器启动后执行初始化方法有3种方式:
1、在bean标签中指定init-method,或者@Bean(initMethod=???)
2、实现InitializingBean接口
3、使用@PostConstruct
这里我们可以利用这个事件实现第4种方式
比如实现ApplicationListener接口,当监听到ContextRefreshedEvent事件时,方法onApplicationEvent(event)
将被调用,然后做一些初始化工作!
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// 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);
}
如果容器刷新异常,会执行这2个方法。
1、destroyBeans():如果容器刷新异常,销毁所有已经创建的单例bean
2、cancelRefresh(ex):设置active=false,表示容器没有刷新过,允许下一次刷新
protected void destroyBeans() {
getBeanFactory().destroySingletons();
}
protected void cancelRefresh(BeansException ex) {
this.active.set(false);
}
不管最终容器有没有成功刷新,都需要清除缓存数据
至此Spring IOC容器已启动完毕,可以愉快地使用啦!
本博客系作者原创,欢迎转载,但请注明出处,谢谢!
原文链接:https://blog.csdn.net/weixin_41042119/article/details/90107989