Spring 容器的初始化流程包含以下几个步骤:

总览

我们知道,启动Spring容器实质上就是创建一个ApplicationContext的上下文对象,这个对象描述了Spring容器的基本操作,包括资源的加载(ResourcePatternResolver)、Bean的创建(ListableBeanFactory, HierarchicalBeanFactory)、应用环境的获取(EnvironmentCapable)、应用的事件推送(ApplicationEventPublisher)等。

而该接口最重要的抽象实现AbstractApplicationContext,因为所有容器的初始化流程都是委托到这个类的refresh()方法中。可以说弄清楚了AbstractApplicationContext.refresh()方法,就弄清楚了Spring容器初始化的整个流程。

由于整个流程涉及的代码和流程分析较多,所以一共会拆分为上中下三篇,本篇为上篇,主要介绍bean工厂的构建与准备。

进入正题

我们首先来看看refresh()方法做了什么

// AbstractApplicationContext.java

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 准备刷新上下文环境
        prepareRefresh();
        // 创建并初始化 BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 填充BeanFactory功能
        prepareBeanFactory(beanFactory);
        try {
            // 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
            postProcessBeanFactory(beanFactory);
            // 激活各种BeanFactory处理器
            invokeBeanFactoryPostProcessors(beanFactory);
            // 注册拦截Bean创建的Bean处理器,即注册 BeanPostProcessor
            registerBeanPostProcessors(beanFactory);
            // 初始化上下文中的资源文件,如国际化文件的处理等
            initMessageSource();
            // 初始化上下文事件广播器
            initApplicationEventMulticaster();
            // 给子类扩展初始化其他Bean
            onRefresh();
            // 在所有bean中查找listener bean,然后注册到广播器中
            registerListeners();
            // 初始化剩下的单例Bean(非延迟加载的)
            finishBeanFactoryInitialization(beanFactory);
            // 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
            finishRefresh();
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
            //  销毁已经创建的Bean
            destroyBeans();
            // 重置容器激活标签
            cancelRefresh(ex);
            // 抛出异常
            throw ex;
        } finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

这里每一个方法都非常重要,需要一个一个地解释说明。

1. prepareRefresh

初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验,如环境变量中必须设置某个值才能运行,否则不能运行,这个时候可以在这里加这个校验,重写 initPropertySources 方法就好了

// AbstractApplicationContext.java

protected void prepareRefresh() {
    // 1、设置启动日期
    this.startupDate = System.currentTimeMillis();
    ///2、设置 context 当前状态
    this.closed.set(false);
    this.active.set(true);

    if (logger.isInfoEnabled()) {
        logger.info("Refreshing " + this);
    }
    // 3、初始化context environment(上下文环境)中的占位符属性来源(在AbstractApplicationContext中是空实现,具体由之类来实现相关逻辑)
    initPropertySources();
    // 4、对属性进行必要的验证
    getEnvironment().validateRequiredProperties();
    // 存储刷新容器前的监听器
    if (this.earlyApplicationListeners == null) {
        this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }
    else {
        // 如果存在早期事件监听器则重置为刷新容器前的事件监听器
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }
    // 初始化早期事件变量
    this.earlyApplicationEvents = new LinkedHashSet<>();
}

该方法主要是做一些准备工作,如:

  1. 设置 context 启动时间

  2. 设置 context 的当前状态

  3. 初始化 context environment 中占位符

    我们可以看到重写了initPropertySources方法的子类有三个,而我们关注

    关于第三点我们以最常用的两个Context来简单说明一下,其中ClassPathXmlApplicationContext作为非Web环境的上下文,AnnotationConfigWebApplicationContext作为Web环境上下文来举例。

    我们可以看到重写了initPropertySources方法的子类有三个,可以看出来ClassPathXmlApplicationContext及其父类都没有重写该方法,即默认情况下Spring提供的标准容器都无需做额外的配置处理。

    那么我们只需关注第一个(AbstractRefreshableWebApplicationContext)的实现即可。

    // AbstractRefreshableWebApplicationContext.java

       @Override
       protected void initPropertySources() {
           // 获取当前的运行环境,如果为空则创建一个运行环境,该方法由AbstractApplicationContext提供默认实现
           ConfigurableEnvironment env = getEnvironment();
           // 如果是Web环境的话就调用子类的initPropertySources方法注册
           if (env instanceof ConfigurableWebEnvironment) {
               ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
           }
       }

       /**
       * 重写了父类的方法,创建了标准的Servlet运行环境
       */

       @Override
       protected ConfigurableEnvironment createEnvironment() {
           return new StandardServletEnvironment();
       }

    // AbstractApplicationContext.java
       @Override
       public ConfigurableEnvironment getEnvironment() {
           if (this.environment == null) {
               // 如果不存在则创建
               this.environment = createEnvironment();
           }
           return this.environment;
       }

       /**
       * 提供默认的标准运行环境(非Web环境)
       */

       protected ConfigurableEnvironment createEnvironment() {
           return new StandardEnvironment();
       }

    关于Web容器的初始化属性并不是本篇的重点,如果有兴趣的可以跟一下代码查询,这里不做深入分析。

  4. 对属性进行必要的验证

    Spring自身的环境并没有强制需要检查某些属性是否存在,这是Spring提供给用户自定义Context时,可检查系统环境中是否存在指定的变量,如果不存在则报错。下面提供一个简单的例子:

    初始化时检查环境中是否存在VAR变量

    public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

       public MyClassPathXmlApplicationContext(String... configLocations) throws BeansException {
           super(configLocations);
       }

       @Override
       protected void initPropertySources() {
           // 添加验证要求
           getEnvironment().setRequiredProperties("VAR");
       }
    }
2. obtainFreshBeanFactory

通知之类刷新内部的BeanFactory,完成BeanDefinition的载入

// AbstractApplicationContext.java

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 刷新 BeanFactory
    refreshBeanFactory();
    // 返回 BeanFactory
    return getBeanFactory();
}

// 抽象方法,由子类实现
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

核心方法就在 #refreshBeanFactory() 方法,该方法的核心任务就是创建 BeanFactory 并对其就行初始化。

标准容器和Web容器都是继承于AbstractRefreshableApplicationContext,那么我们只关注该类的实现即可。先看看执行流程:

再结合源码查看:

// AbstractRefreshableApplicationContext.java

@Override
protected final void refreshBeanFactory() throws BeansException {
    // 若已有 BeanFactory ,销毁它的 Bean 们,并销毁 BeanFactory
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 创建 BeanFactory 对象
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        // 指定序列化编号
        beanFactory.setSerializationId(getId());
        // 定制 BeanFactory 设置相关属性
        customizeBeanFactory(beanFactory);
        // 加载 BeanDefinition
        loadBeanDefinitions(beanFactory);
        // 设置 Context 的 BeanFactory
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    } catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}
// 创建Bean工厂
protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
// 配置工厂信息
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    if (this.allowBeanDefinitionOverriding != null) {
        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.allowCircularReferences != null) {
        beanFactory.setAllowCircularReferences(this.allowCircularReferences);
    }
}
// 加载BeanDefinition,抽象方法,由子类实现
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
            throws BeansException, IOException
;
  1. 判断当前容器是否存在一个 BeanFactory,如果存在则对其进行销毁和关闭
  2. 调用 #createBeanFactory() 方法,创建一个 BeanFactory 实例,其实就是创建 DefaultListableBeanFactory
  3. 配置 BeanFactory
  4. 加载 BeanDefinition
  5. 将创建好的 bean 工厂的引用交给的 context 来管理

其中第4点是Spring容器的关键步骤之一,它实现了载入我们配置的xml文件或者注解配置,并将其解析为BeanDefinition,以供创建Bean使用。我们来仔细看看它是如何实现的:

2.1 loadBeanDefinitions

同样我们只关注前两个即可:

  • AbstractXmlApplicationContext: 解析xml配置
  • AnnotationConfigWebApplicationContext 解析注解配置

2.1.1 AbstractXmlApplicationContext的实现

同样,先看流程:

// AbstractXmlApplicationContext.java

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 创建 XmlBeanDefinitionReader 对象
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // 对 XmlBeanDefinitionReader 进行环境变量的设置
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // 对 XmlBeanDefinitionReader 进行设置,可以进行覆盖
    initBeanDefinitionReader(beanDefinitionReader);

    // 从 Resource 们中,加载 BeanDefinition 们
    loadBeanDefinitions(beanDefinitionReader);
}

// 把Location资源转换同Spring统一的Resource,并调用#loadBeanDefinitions(Resource resource)方法进行资源加载
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
        reader.loadBeanDefinitions(configResources);
    }
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        reader.loadBeanDefinitions(configLocations);
    }
}

// 真正进行资源载入的方法由子类提供
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

关于AbstractXmlApplicationContext#loadBeanDefinitions我们点到为止,在这一步我们只要知道它做的是解析应用配置文件中<beans /><bean /><import /><alias />等Spring bean核心标签为BeanDefinition外,还会解析<tx:xxx /><context:xxx />等Spring自定义标签来开启更丰富的Spring功能。

关于Spring是如何定位配置文件,如何加载解析Resource资源,可以查看《Spring IOC 之 Resource体系》,里面会详细解析资源定位,解析,和BeanDefinition注册的过程。

2.1.2 AnnotationConfigWebApplicationContext 的实现

// TODO

3. prepareBeanFactory

配置工厂的功能,例如类加载器和后置处理器等。

上面获取获取的 BeanFactory 除了加载了一些 BeanDefinition 就没有其他任何东西了,这个时候其实还不能投入生产,因为还少配置了一些东西,比如 context的 ClassLoader 和 后置处理器等等。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 设置beanFactory的classLoader
    beanFactory.setBeanClassLoader(getClassLoader());
    // 设置beanFactory的表达式语言处理器,Spring3开始增加了对语言表达式的支持,默认可以使用#{bean.xxx}的形式来调用相关属性值
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    // 为beanFactory增加一个默认的propertyEditor
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // 添加ApplicationContextAwareProcessor
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    // 设置忽略自动装配的接口
    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.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // 将早期用于检测内部bean的后置处理器注册为applicationlistener。
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // 增加对AspectJ的支持
    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()));
    }

    // 注册默认的系统环境bean
    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());
    }
}

看上面的源码知道这个就是对 BeanFactory 设置各种各种的功能。

4. postProcessBeanFactory

提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess

// AbstractApplicationContext.java

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 由之类提供实现
}

重写了该方法的子类如下:

类似的,我们只关注第一个,即Web容器对BeanFactory做了进一步的处理,而标准容器没有对BeanFactory做额外的定制。

// AbstractRefreshableWebApplicationContext.java

@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 添加 ServletContextAwareProcessor 到 BeanFactory 容器中,该 processor 实现 BeanPostProcessor 接口,主要用于将ServletContext 传递给实现了 ServletContextAware 接口的 bean
    beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
    // 忽略 ServletContextAware、ServletConfigAware
    beanFactory.ignoreDependencyInterface(ServletContextAware.class);
    beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
    // 注册 WEB 应用特定的域(scope)到 beanFactory 中,以便 WebApplicationContext 可以使用它们。比如 “request” , “session” , “globalSession” , “application”
    WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
    // 注册 WEB 应用特定的 Environment bean 到 beanFactory 中,以便WebApplicationContext 可以使用它们。如:”contextParameters”, “contextAttributes”
    WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}

结语

至此,BeanFactory可以说是属性该设置的设置了,配置该初始化的也初始化了,接下来一鼓作气,继续深入 Spring-容器初始化流程(中)