Spring IoC - IoC 容器初始化 源码解析

undefined

前言

本章主要内容是由以下部分组成,

  • Spring 中容器初始化入口
  • 以最经典的ClassPathXmlApplicationContext 为例,讲解Spring IoC 的容器初始化过程

在学习源码的过程当中,我想强调两点:

  1. 一定要学会抓重点,归纳核心类、核心方法、核心步骤。
  2. 理解类、变量、方法名的命名,Spring 源码的命名是很讲究的,很多时候是自解释的
  3. 一定要学会看Java doc ,同上,这种顶级的框架的java doc 描述非常的详尽

Spring 容器初始化入口

启动容器,实际上指的就是实例化ApplicationContext的这个动作。只是在不同情况下可能有不同的表现形式。

  1. ClassPathXmlApplicationContext 通过XML配置
ApplicationContext context = new ClassPathXmlApplicationContext(applicationContext.xml");
  1. AnnotationConfigApplicationContext 通过java config 类配置
@Configuration
@ComponentScan("ric.study.demo.ioc")
public class BeanDemoConfig {
    public static void main(String... strings) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(BeanDemoConfig.class);
        System.out.println("Spring container started and is ready");
        ...
    }
}

类似前面这两种new ***ApplicationContext的方式,很少会用于直接的生产开发。一般都是我们自己在demo中或者单元测试中会用到。

  1. WebApplicationContext SpringMVC

这个实际上是我们平常最常用的初始化方式,Spring MVC 中 ServletContext 为 Spring 的 IoC容器提供了宿主环境。是通过ContextLoaderListener 的初始化来建立的。

WebApplicationContext 的初始化调用链路:ContextLoaderListener.contextInitialized --> ContextLoader.initWebApplicationContext --> ContextLoader.createWebApplicationContext --> ContextLoader.determineContextClass --> ContextLoader.determineContextClass。

底层是通过反射来实例化的。

    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);
    }

这块内容先简要提一下,属于SpringMVC的内容,不是我们今天要讲的Spring IoC 模块的知识。

容器初始化 源码解析

现在让我们正式开始的源码解读。会从最经典的ClassPathXmlApplicationContext 上下文为起点,来描述整个过程。

在说明之前,我想了想还是觉得把整个IoC容器初始化的关键步骤为大家梳理一下,以便于大家能在心里有个大概的脉络,更容易读懂源码,更容易抓住重点。再重复提一句,看源码一定要学会抓重点,归纳核心类、核心方法、核心步骤。

ClassPathXmlApplicationContext 的容器初始化我们大致分为下面几步:

  1. BeanDefinition 的 Resource 定位

    这里的Resource定位 是通过继承ResourceLoader 获得的,ResourceLoader代表了加载资源的一种方式,正是策略模式的实现

  2. 从 Resource中解析、载入BeanDefinition

  3. BeanDefinition 在IoC 容器中的注册

前面说了,实例化这个上下文,就是在启动 IoC 容器。那我们肯定要从它的构造函数入手。

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

入参中的configLocations在这里就是你XML配置文件 的 classpath。

setConfigLocations(configLocations);我这里不展开讲,内容不复杂,就是把一些带有占位符的地址解析成实际的地址。

再之后就是refresh(),我们说的容器初始化,就是在这里面进行的,这里取名为refresh,是因为容器启动之后,再调用refresh()会刷新IoC 容器

这里先放上IoC容器初始化的时序图,方便理解,

loadBeanDefinitions.png

refresh() 的源码:

@Override
public void refresh() throws BeansException, IllegalStateException {
   // 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
   synchronized (this.startupShutdownMonitor) {

      // 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
      prepareRefresh();

      // 这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中,
      // 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
      // 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
      // 这块待会会展开说
      prepareBeanFactory(beanFactory);

      try {
         // 【这里需要知道 BeanFactoryPostProcessor 这个知识点,Bean 如果实现了此接口,
         // 那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。】

         // 这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化
         // 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事
         postProcessBeanFactory(beanFactory);
         // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
         // 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
         registerBeanPostProcessors(beanFactory);

         // 初始化当前 ApplicationContext 的 MessageSource,国际化不是重点,不展开
         initMessageSource();

         // 初始化当前 ApplicationContext 的事件广播器,这里也不展开了
         initApplicationEventMulticaster();

         // 从方法名就可以知道,典型的模板方法(钩子方法),
         // 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
         onRefresh();

         // 注册事件监听器,监听器需要实现 ApplicationListener 接口。这也不是我们的重点,过
         registerListeners();

         // 重点,重点,重点
         // 初始化所有的 singleton beans
         //(lazy-init 的除外)
         finishBeanFactoryInitialization(beanFactory);

         // 最后,广播事件,ApplicationContext 初始化完成
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         // 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
         destroyBeans();
         // Reset 'active' flag.
         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() 创建容器前的准备工作
  2. obtainFreshBeanFactory() 创建 BeanFactory
  3. prepareBeanFactory(beanFactory) 对BeanFactory进行一些特征的设置工作
  4. finishBeanFactoryInitialization(beanFactory); 初始化所有的 singleton beans(DI的入口)

1. prepareRefresh() 创建容器前的准备工作

protected void prepareRefresh() {
   // 记录启动时间,
   // 将 active 属性设置为 true,closed 属性设置为 false,它们都是 AtomicBoolean 类型
   this.startupDate = System.currentTimeMillis();
   this.closed.set(false);
   this.active.set(true);

   if (logger.isInfoEnabled()) {
      logger.info("Refreshing " + this);
   }

   // Initialize any placeholder property sources in the context environment
   initPropertySources();

   // 校验 xml 配置文件
   getEnvironment().validateRequiredProperties();

   this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

2.★ obtainFreshBeanFactory() 创建 Bean 容器,加载并注册 Bean

IoC初始化里面最重要的部分。

关键是以下几步,

  • 初始化BeanFactory
  • 加载Bean
  • 注册Bean
  • ...

注意:这步完成后,Bean 并没有完成初始化,实际的实例并没有被创建。

源码位置:AbstractApplicationContext#obtainFreshBeanFactory()

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        // 关闭旧的 BeanFactory (如果有),创建新的 BeanFactory,加载 Bean 定义、注册 Bean 等等
        refreshBeanFactory();
        // 返回上一步刚刚创建的BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

源码位置:AbstractRefreshableApplicationContext#refreshBeanFactory()

    protected final void refreshBeanFactory() throws BeansException {
        // 如果 ApplicationContext 已经加载过 BeanFactory,销毁所有的Bean,关闭BeanFactory
        // 注意点:应用中BeanFactory是可以有多个的,这里可不是说全局是否有BeanFactory
        // 而是说当前的ApplicationContext有没有BeanFactory!
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            // 初始化一个 DefaultListableBeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            // 用于 BeanFactory 的序列化,一般人应该用不到吧...
            beanFactory.setSerializationId(getId());
            // 下面是两个重点方法
            // 设置 BeanFactory 的两个重要属性
            // 是否允许 Bean 覆盖、是否允许循环引用 TODO 2.1
            customizeBeanFactory(beanFactory);
            
            // 加载BeanDefinition到BeanFactory  单独拉出来讲
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

看到这里的时候,可以感觉到一个设计思路,ApplicationContext 继承自 BeanFactory,但是它不应该被理解为 BeanFactory 的实现类,而是说其内部持有一个实例化的 BeanFactory(DefaultListableBeanFactory)。以后所有的 BeanFactory 相关的操作其实是委托给这个实例来处理的

2.1 customizeBeanFactory

    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

BeanDefinition 的覆盖问题可能会有开发者碰到这个坑,就是在配置文件中定义 bean 时使用了相同的 id 或 name,默认情况下,allowBeanDefinitionOverriding 属性为 null(Boolean类型),如果在同一配置文件中重复了,会抛错,但是如果不是同一配置文件中,会发生覆盖

循环引用也很好理解:A 依赖 B,而 B 依赖 A。或 A 依赖 B,B 依赖 C,而 C 依赖 A。

默认情况下,Spring 允许循环依赖,当然如果你在 A 的构造方法中依赖 B,在 B 的构造方法中依赖 A 是不行的。

2.2 ★ loadBeanDefinitions(beanFactory) 加载BeanDefinition

看下这个方法的声明,

    /**
     * Load bean definitions into the given bean factory, typically through
     * delegating to one or more bean definition readers.
     * @param beanFactory the bean factory to load bean definitions into
     * @throws BeansException if parsing of the bean definitions failed
     * @throws IOException if loading of bean definition files failed
     * @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
     */
    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
            throws BeansException, IOException;

在ClassPathXmlApplicationContext 是按照解析XML的加载方式。看javadoc的描述,是通过XmlBeanDefinitionReader来载入Bean Definitions。

    /**
     * Loads the bean definitions via an XmlBeanDefinitionReader.
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
     * @see #initBeanDefinitionReader
     * @see #loadBeanDefinitions
     */
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // 初始化Reader 不重要,看下这个方法的javadoc就很好理解了
        initBeanDefinitionReader(beanDefinitionReader);
        // 真正重要的步骤!!
        // 用Reader去加载XML配置
        loadBeanDefinitions(beanDefinitionReader);
    }

loadBeanDefinitions(beanDefinitionReader)

    /**
     * Load the bean definitions with the given XmlBeanDefinitionReader.
     * 看这句注释:this method is just supposed to load and/or register bean definitions.
     */
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            // 这个分支,通过路径名去获取Resource,会和上面的方法殊途同归
            reader.loadBeanDefinitions(configLocations);
        }
    }

AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources)

    @Override
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        for (Resource resource : resources) {
            // 遍历解析XML文件,加载 BeanDefinition
            counter += loadBeanDefinitions(resource);
        }
        return counter;
    }

接下去的源码不细讲,这里载入分为两大步,

  1. 一就是通过调用XML的解析器获取到 document 对象,完成通用XML解析;
  2. 二就是按照Spring的Bean规则进行解析。Spring的Bean规则进行解析这个过程是BeanDefinitionDocumentReader来实现的,里面包含了各种Spring Bean定义规则的处理。

这里我觉得核心知识点就是Spring Bean规则的解析,简单点来说,里面包含了我们在XML配置的那些信息,怎么解析成容器中 BeanDefinition的规则和步骤。这部分由于和主要流程关系不大,我就没贴源码解析了,会占掉很大的篇幅,影响阅读和理解。

在这因为Spring 的 Bean配置方式有很多,解析配置信息到BeanDefinition的实现方式也有很多,XML又是现在少用的方式,所以关于XML中的Spring Bean规则的解析的详细源码就先略过了。有兴趣的同学可以阅读《Spring 技术内幕》这本书或者其他的文章书籍。

2.3 注册Bean

虽然上面说了不讲XML 解析 成 BeanDefinition的过程源码。但是上述loadBeanDefinitions(resource)包含了我们关键的第三步,注册Bean。这部分还是需要填一下的。

注意一下前面实例化Reader的代码,

    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    /**
     * Create new XmlBeanDefinitionReader for the given bean factory.
     * @param registry the BeanFactory to load bean definitions into,
     * in the form of a BeanDefinitionRegistry
     */
    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

beanDefinitionReader 获取到 beanFactory 的引用,这个引用会在beanDefinition 被加载完毕要注册的时候使用到。可以看到是因为BeanDefinitionRegistry这个接口,赋予了BeanFactory注册BeanDefinition的特性。

具体执行“注册Bean”这一动作的源码,按照上述loadBeanDefinitions(resource)方法一直走下去的话是在DefaultBeanDefinitionDocumentReader#processBeanDefinition()方法中,

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                // 注册Bean
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

源码位置BeanDefinitionReaderUtils#registerBeanDefinition()

    /**
     * Register the given bean definition with the given bean factory.
     * @param definitionHolder the bean definition including name and aliases
     * @param registry the bean factory to register with
     * @throws BeanDefinitionStoreException if registration failed
     */
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        // 注册
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        // 如果还有别名的,把别名全都注册一遍
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                // 到时候获取的时候,就是先把Alias转化成BeanName,再去获取对应的Bean
                registry.registerAlias(beanName, alias);
            }
        }
    }

上面的registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());,

源码位置DefaultListableBeanFactory#registerBeanDefinition()

@Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        // 注意哦,这里是有关 “允许Bean覆盖” 的逻辑代码
        // 记得这个配置 allowBeanDefinitionOverriding
        BeanDefinition oldBeanDefinition;

        // beanDefinitionMap 是存放所有BeanDefinition的容器
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        
        // not null 说明,有重复名称的bean
        if (oldBeanDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                // 判断是否允许覆盖,不允许直接抛异常
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        "': There is already [" + oldBeanDefinition + "] bound.");
            }
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // 打下debug log...用框架定义的 Bean 覆盖用户自定义的 Bean
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(oldBeanDefinition)) {
                // 打下debug log...用新的 Bean 覆盖旧的 Bean
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                // 打下debug log...用同等的 Bean 覆盖旧的 Bean
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            // 覆盖了
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            // 判断是否有其他Bean已经开始初始化了
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                // 检测创建 Bean 阶段已经开启,需要对 beanDefinitionMap 进行并发控制
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // Still in startup registration phase
                // 最最最正常的分支
                
                //  注册 到 容器中
                this.beanDefinitionMap.put(beanName, beanDefinition);
                // 这是一个 ArrayList,所以会按照 bean 配置的顺序保存每一个注册的 Bean 的名字
                this.beanDefinitionNames.add(beanName);
                // 这是个 LinkedHashSet,代表的是手动注册的 singleton bean,
                // 注意这里是 remove 方法,到这里的 Bean 当然不是手动注册的
                // 手动指的是通过调用以下方法注册的 bean :
                // registerSingleton(String beanName, Object singletonObject)
                // 这不是重点,解释只是为了不让大家疑惑。Spring 会在后面"手动"注册一些 Bean,
                // 如 "environment"、"systemProperties" 等 bean,我们自己也可以在运行时注册 Bean 到容器中的
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

以上,只是obtainFreshBeanFactory()的内容,到这里,BeanFactory也算是实例化完成了。


这里还是来个分割线。因为接下去会讲refresh() 方法的后续步骤的知识点,我想读者同学在这里最好最好,回到 前面refresh()总述的部分,再看一下。(如果你是一边还打开着IDE,在对照阅读调试的话,回到最前面refresh() 方法,再继续往下)

3. prepareBeanFactory(beanFactory)

此方法负责对BeanFactory进行一些特征的设置工作,这些特征在代码中都有体现。

    /**
     * Configure the factory's standard context characteristics,
     * such as the context's ClassLoader and post-processors.
     * @param beanFactory the BeanFactory to configure
     */
    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Tell the internal bean factory to use the context's class loader etc.
        // BeanFactory 需要加载类,所以需要获得类加载器
        // 设置当前ApplicationContext的类加载器
        beanFactory.setBeanClassLoader(getClassLoader());
        // 内含 Spel 解释器,暂时不重要
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        // 注册属性编辑器,暂时不重要
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        // Configure the bean factory with context callbacks.
        // 添加一个ApplicationContextAwareProcessor,主要针对实现了Aware接口的Bean
        // 延伸知识:在Spring中我们自己的bean可以通过实现EnvironmentAware等一系列Aware接口,获取到Spring内部的一些对象。
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        // 依赖解析忽略, 设置哪些接口在进行依赖注入的时候应该被忽略
        // 通俗来说,下面几行的意思就是,如果某个 bean 依赖于以下几个接口的实现类,在自动装配的时候忽略它们,
        // Spring 会通过其他方式来处理这些依赖。
        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 赋值,如果有 bean 依赖了以下几个,会注入这边相应的值,
        * 之前我们说过,"当前 ApplicationContext 持有一个 BeanFactory",这里解释了第一行
        * ApplicationContext 还继承了 ResourceLoader、ApplicationEventPublisher、MessageSource
        * 所以对于这几个依赖,可以赋值为 this,注意 this 是一个 ApplicationContext
        * 那这里怎么没看到为 MessageSource 赋值呢?那是因为 MessageSource 被注册成为了一个普通的 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 也很简单,在 bean 实例化后,如果是 ApplicationListener 的子类,
        // 这个postProcessor的作用就是将其添加到 listener 列表中,可以理解成:注册 事件监听器
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // Detect a LoadTimeWeaver and prepare for weaving, if found.
        // 这里涉及到特殊的 bean,名为:loadTimeWeaver,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()));
        }
        // Register default environment beans.
        // Spring 的“智能”操作,会帮我们默认注册一些有用的Bean
        //  如果没有定义 "environment" 这个 bean,那么 Spring 会 "手动" 注册一个
        if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        // 如果没有定义 "systemProperties" 这个 bean,那么 Spring 会 "手动" 注册一个
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        // 如果没有定义 "systemEnvironment" 这个 bean,那么 Spring 会 "手动" 注册一个
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
    }

4. ★ finishBeanFactoryInitialization(beanFactory) 实例化所有单例

这里会负责初始化所有的 singleton beans。

Spring 会在这个阶段完成所有的 singleton beans 的实例化。

到目前为止,应该说 BeanFactory 已经创建完成,并且所有的实现了 BeanFactoryPostProcessor 接口的 Bean 都已经初始化并且其中的 postProcessBeanFactory(factory)方法已经得到回调执行了。而且 Spring 已经“手动”注册了一些特殊的 Bean,如 environmentsystemProperties 等。

剩下的就是初始化 singleton beans 了,我们知道它们是单例的,如果没有设置懒加载,那么 Spring 会在接下来初始化所有的 singleton beans。

源码位置:AbstractApplicationContext#finishBeanFactoryInitialization()

/**
     * Finish the initialization of this context's bean factory,
     * initializing all remaining singleton beans.
     */
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // Initialize conversion service for this context.
        // 初始化"conversionService"的bean,此接口用于类型之间的转化,不是重点,放过我,自己去看
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    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(new StringValueResolver() {
                @Override
                public String resolveStringValue(String strVal) {
                    return 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) {
            // 先初始化 LoadTimeWeaverAware 类型的bean
            getBean(weaverAwareName);
        }

        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);

        // Allow for caching all bean definition metadata, not expecting further changes.
        // 看方法名就知道了,冻结所有BeanDefinition的元数据了
        // 没什么别的目的,因为到这一步的时候,Spring 已经开始预初始化 singleton beans 了,
        // 肯定不希望这个时候还出现 bean 定义解析、加载、注册。
        beanFactory.freezeConfiguration();

        // Instantiate all remaining (non-lazy-init) singletons.
        // 开始初始化,进来看吧,重点在里面
        beanFactory.preInstantiateSingletons();
    }

源码位置:DefaultListableBeanFactory#preInstantiateSingletons()

    @Override
    public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Pre-instantiating singletons in " + this);
        }
        
        // copy 一个包含所有BeanName的集合副本
        List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

        // 触发所有非懒加载的 singleton beans的初始化
        for (String beanName : beanNames) {
            // Bean 可能有继承parent的关系,获取合并后的RootBeanDefinition
            // 这个知识点用的很少的
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            
            // 非抽象、非懒加载的singletons
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                // FactoryBean知识点,不了解的看另一篇文章或者自己google
                if (isFactoryBean(beanName)) {
                    // FactoryBean 会在 beanName前面加前缀"&"
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                    boolean isEagerInit;
                    // SmartFactoryBean, 非重点,没深入了解,放过我
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                            @Override
                            public Boolean run() {
                                return ((SmartFactoryBean<?>) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                else {
                    // 正常的bean都到这里来了,重点哦,里面进行初始化了
                    // 调用链很复杂,单独拉出来讲,先继续
                    getBean(beanName);
                }
            }
        }
        
        // 前面流程走完,说明所有的非懒加载singletonBean 完成了初始化

        // Trigger post-initialization callback for all applicable beans...
        // 看注释就懂了,如果我们定义的 bean 是实现了 SmartInitializingSingleton 接口的,
        // 那么在这里得到回调,忽略它吧。
        for (String beanName : beanNames) {
            Object singletonInstance = getSingleton(beanName);
            if (singletonInstance instanceof SmartInitializingSingleton) {
                final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                if (System.getSecurityManager() != null) {
                    AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        @Override
                        public Object run() {
                            smartSingleton.afterSingletonsInstantiated();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                else {
                    smartSingleton.afterSingletonsInstantiated();
                }
            }
        }
    }

小知识点 关于 lazy-init

ApplicationContext 实现的默认行为就是在启动时将所有 singleton bean提前进行实例化。提前实例化意味着作为初始化过程的一部分,ApplicationContext 实例会创建并配置所有的singleton bean。通常情况下这是件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)

有时候这种默认处理可能并不是你想要的。如果你不想让一个singleton bean 在ApplicationContext 实现在初始化时被提前实例化,那么可以将bean设置为延迟实例化。一个延迟初始化bean 将告诉IoC 容器是在启动时还是在第一次被用到时实例化。

需要说明的是,如果一个bean被设置为延迟初始化,而另一个非延迟初始化的singleton bean 依赖于它,那么当ApplicationContext 提前实例化singleton bean时,它必须也确保所有上述singleton 依赖bean也被预先初始化,当然也包括设置为延迟实例化的bean。因此,如果Ioc容器在启动的时候创建了那些设置为延迟实例化的bean的实例,你也不要觉得奇怪,因为那些延迟初始化的bean可能在配置的某个地方被注入到了一个非延迟初始化singleton bean里面。

结语

以上,本文就是关于Spring IoC 容器初始化的主要内容。

Spring IoC 的设计中,Bean定义的解析和Bean的依赖注入,是两个独立的过程,前面所有内容讲的就是IoC容器的初始化,资源定位、载入以及解析BeanDefinition并且注册。

最后一步的实例化所有单例,引入了getBean()方法,这就是Spring IoC 依赖注入的入口。也是下节源码解读的主要内容。

另外说一句,上面的源码解析,肯定不会是完备的,只是提取了我认为重要的东西。

如有疏漏,敬请谅解和自己查阅相关资料学习。如果错误,敬请指正!

本文由博客一文多发平台 OpenWrite 发布!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容