赞
踩
众所周知,Spring有2大核心,IOC和AOP,而IOC又是AOP的基础,因此Spring源码学习系列的第一篇博客以IOC开始。本篇博客以IOC容器的启动流程为切入点,介绍从web容器启动,Spring ContextLoaderListener监听到ServletContextEvent并初始化WebApplicationContext到最终WebApplicationContext初始化完毕存入ServletContext的大致流程。
Spring容器有多种启动方式,这里以常见web.xml配置为例,介绍web容器是如何启动Spring容器的。
在web.xml中,通常能看到下面配置
这里干了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<ApplicationListener<?>> */ 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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。