7. IoC容器的初始化过程

​ 简单来说,IoC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IoC容器的正式启动。具体来说,这个启动包括BeanDefinition的Resouce定位、载入和注册三个基本过程。如果我们了解如何编程式地使用IoC容器,就可以清楚地看到Resource定位和载入过程的接口调用。在下面的内容里,我们将会详细分析这三个过程的实现。

​ 在分析之前,要提醒读者注意的是,Spring把这三个过程分开,并使用不同的模块来完成,如使用相应的ResourceLoader、BeanDefinitionReader等模块,通过这样的设计方式,可以让用户更加灵活地对这三个过程进行剪裁或扩展,定义出最适合自己的IoC容器的初始化过程。

  • 第一个过程是Resource定位过程。这个Resource定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。对于这些BeanDefinition的存在形式,相信大家都不会感到陌生。比如,在文件系统中的Bean定义信息可以使用FileSystemResource来进行抽象;在类路径中的Bean定义信息可以使用前面提到的ClassPathResource来使用,等等。这个定位过程类似于容器寻找数据的过程,就像用水桶装水先要把水找到一样。

  • 第二个过程是BeanDefinition的载入。这个载入过程是把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。下面介绍这个数据结构的详细定义。具体来说,这个BeanDefinition实际上就是POJO对象在IoC容器中的抽象,通过这个BeanDefinition定义的数据结构,使IoC容器能够方便地对POJO对象也就是Bean进行管理。在下面的章节中,我们会对这个载入的过程进行详细的分析,使大家对整个过程有比较清楚的了解。

  • 第三个过程是向IoC容器注册这些BeanDefinition的过程。这个过程是通过调用BeanDefinitionRegistry接口的实现来完成的。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过分析,我们可以看到,在IoC容器内部将BeanDefinition注入到一个HashMap中去,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。

​ 值得注意的是,这里谈的是IoC容器初始化过程,在这个过程中,一般不包含Bean依赖注入的实现。在Spring IoC的设计中,Bean定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候。但有一个例外值得注意,在使用IoC容器时有一个预实例化的配置,通过这个预实例化的配置(具体来说,可以通过为Bean定义信息中的lazyinit属性),用户可以对容器初始化过程作一个微小的控制,从而改变这个被设置了lazyinit属性的Bean的依赖注入过程。举例来说,如果我们对某个Bean设置了lazyinit属性,那么这个Bean的依赖注入在IoC容器初始化时就预先完成了,而不需要等到整个初始化完成以后,第一次使用getBean时才会触发。

​ 了解了IoC容器进行初始化的大致轮廓之后,下面我们详细地介绍在IoC容器的初始化过程中,BeanDefinition的资源定位、载入和解析过程是怎么实现的。

7.1 BeanDefinition的Resource定位

​ 以编程的方式使用DefaultListableBeanFactory(创建一个BeanFactory)时,首先定义一个Resource来定位容器使用的BeanDefinition。这时使用的是ClassPathResource,这意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition信息。

ClassPathResource res = new ClassPathResource("beans.xml");
这里定义的Resource并不能由DefaultListableBeanFactory直接使用,Spring通过BeanDefinitionReader来对这些信息进行处理。在这里,我们也可以看到使用Application-Context相对于直接使用DefaultListableBeanFactory的好处。因为在ApplicationContext中,Spring已经为我们提供了一系列加载不同Resource的读取器的实现,而DefaultListableBeanFactory只是一个纯粹的IoC容器,需要为它配置特定的读取器才能完成这些功能。当然,有利就有弊,使用DefaultListableBeanFactory这种更底层的容器,能提高定制IoC容器的灵活性。

​ 回到我们经常使用的ApplicationContext上来,例如FileSystemXmlApplicationContext、ClassPathXmlApplicationContext以及XmlWebApplicationContext等。简单地从这些类的名字上分析,可以清楚地看到它们可以提供哪些不同的Resource读入功能,比如

​ FileSystemXmlApplicationContext可以从文件系统载入Resource,ClassPathXmlApplication-Context可以从Class Path载入Resource,XmlWebApplicationContext可以在Web容器中载入Resource,等等。

​ 下面以FileSystemXmlApplicationContext为例,通过分析这个ApplicationContext的实现来看看它是怎样完成这个Resource定位过程的。作为辅助,我们可以在图2-5中看到相应的ApplicationContext继承体系。

图2-5 FileSystemXmlApplicationContext的继承体系

​ 从源代码实现的角度,我们可以近距离关心以FileSystemXmlApplicationConext为核心的继承体系,如图2-6所示。

图2-6 源代码角度的FileSystemXmlApplicationContext的继承关系

​ 从图2-6中可以看到,这个FileSystemXmlApplicationContext已经通过继承Abstract-ApplicationContext具备了ResourceLoader读入以Resource定义的BeanDefinition的能力,因为AbstractApplicationContext的基类是DefaultResourceLoader。下面让我们看看FileSystemXmlApplicationContext的具体实现,如代码清单2-4所示。

代码清单2-4 FileSystemXmlApplicationContext的实现

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {public FileSystemXmlApplicationContext() {}public FileSystemXmlApplicationContext(ApplicationContext parent) {super(parent);}//这个构造函数的configLocation包含的是BeanDefinition所在的文件路径public FileSystemXmlApplicationContext(String configLocation) throws BeansException {this(new String[]{configLocation}, true, (ApplicationContext)null);}//这个构造函数允许configLocation包含多个BeanDefinition的文件路径public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {this(configLocations, true, (ApplicationContext)null);}//这个构造函数在允许configLocation包含多个BeanDefinition的文件路径的同时,还允许指定//自己的双亲IoC容器public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {this(configLocations, true, parent);}public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {this(configLocations, refresh, (ApplicationContext)null);}//在对象的初始化过程中,调用refresh函数载入BeanDefinition,这个refresh启动了//BeanDefinition的载入过程,我们会在下面进行详细分析public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {super(parent);this.setConfigLocations(configLocations);if (refresh) {this.refresh();}}//这是应用于文件系统中Resource的实现,通过构造一个FileSystemResource来得到一个在文件//系统中定位的BeanDefinition       //这个getResourceByPath是在BeanDefinitionReader的loadBeanDefintion中被调用的//loadBeanDefintion采用了模板模式,具体的定位实现实际上是由各个子类来完成的protected Resource getResourceByPath(String path) {if (path.startsWith("/")) {path = path.substring(1);}return new FileSystemResource(path);}
}

​ 在FileSystemApplicationContext中,我们可以看到在构造函数中,实现了对configuration进行处理的功能,让所有配置在文件系统中的,以XML文件方式存在的BeanDefnition都能够得到有效的处理,比如,实现了getResourceByPath方法,这个方法是一个模板方法,是为读取Resource服务的。对于IoC容器功能的实现,这里没有涉及,因为它继承了AbstractXmlApplicationContext,关于IoC容器功能相关的实现,都是在FileSystemXmlApplicationContext中完成的,但是在构造函数中通过refresh来启动IoC容器的初始化,这个refresh方法非常重要,也是我们以后分析容器初始化过程实现的一个重要入口。
​ 注意 FileSystemApplicationContext是一个支持XML定义BeanDefinition的ApplicationContext,并且可以指定以文件形式的BeanDefinition的读入,这些文件可以使用文件路径和URL定义来表示。在测试环境和独立应用环境中,这个ApplicationContext是非常有用的。
​ 根据图2-7的调用关系分析,我们可以清楚地看到整个BeanDefinition资源定位的过程。这个对BeanDefinition资源定位的过程,最初是由refresh来触发的,这个refresh的调用是在FileSystemXmlBeanFactory的构造函数中启动的,大致的调用过程如图2-8所示。

图2-7 getResourceByPath的调用关系

图2-8 getResourceByPath的调用过程
从Spring源代码实现的角度,我们可以通过debug的功能,查看详细的方法调用栈,如图2-7所示。
大家看了上面的调用过程可能会比较好奇,这个FileSystemXmlApplicationContext在什么地方定义了BeanDefinition的读入器BeanDefinitionReader,从而完成BeanDefinition信息的读入呢?在前面分析过,在IoC容器的初始化过程中,BeanDefinition资源的定位、读入和注册过程是分开进行的,这也是解耦的一个体现。关于这个读入器的配置,可以到

​ FileSystemXmlApplicationContext的基类AbstractRefreshableApplicationContext中看看它是怎样实现的。

​ 我们重点看看AbstractRefreshableApplicationContext的refreshBeanFactory方法的实现,这个refreshBeanFactory被FileSystemXmlApplicationContext构造函数中的refresh调用(obtainFreshBeanFactory();刷新BeanFactory,关闭之前的bean factory,然后初始化新的bean factory, loadBeanDefinitions来载入BeanDefinition)。在这个方法中,通过createBeanFactroy构建了一个IoC容器供ApplicationContext使用。这个IoC容器就是我们前面提到过的DefaultListableBeanFactory,同时,它启动了loadBeanDefinitions来载入BeanDefinition,这个过程和前面以编程式的方法来使用IoC容器(XmlBeanFactory)的过程非常类似。
​ 从代码清单2-4中可以看到,在初始化FileSystmXmlApplicationContext的过程中,通过IoC容器的初始化的refresh来启动整个调用,使用的IoC容器是DefultListableBeanFactory。具体的资源载入在XmlBeanDefinitionReader读入BeanDefinition时完成,在XmlBeanDefinitionReader的基类AbstractBeanDefinitionReader中可以看到这个载入过程的具体实现。对载入过程的启动,可以在AbstractRefreshableApplicationContext的loadBeanDefinitions方法中看到,如代码清单2-5所示。

代码清单2-5 AbstractRefreshableApplication

protected final void refreshBeanFactory() throws BeansException {//这里判断,如果已经建立了BeanFactory,则销毁并关闭该BeanFactoryif (this.hasBeanFactory()) {this.destroyBeans();this.closeBeanFactory();}//这里是创建并设置持有的DefaultListableBeanFactor的地方同时调用//loadBeanDefinitions再载入BeanDefinition的信息try {DefaultListableBeanFactory beanFactory = this.createBeanFactory();beanFactory.setSerializationId(this.getId());this.customizeBeanFactory(beanFactory);this.loadBeanDefinitions(beanFactory);synchronized(this.beanFactoryMonitor) {this.beanFactory = beanFactory;}} catch (IOException var5) {throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);}
}

7.2 BeanDefinition的载入和解析

​ 在完成对代表BeanDefinition的Resource定位的分析后,下面来了解整个BeanDefinition信息的载入过程。对IoC容器来说,这个载入过程,相当于把定义的BeanDefinition在IoC容器中转化成一个Spring内部表示的数据结构的过程。IoC容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关操作来完成的。这些BeanDefinition数据在IoC容器中通过一个HashMap来保持和维护。当然这只是一种比较简单的维护方式,如果需要提高IoC容器的性能和容量,完全可以自己做一些扩展。

​ 下面,从DefaultListableBeanFactory的设计入手,看看IoC容器是怎样完成BeanDefinition载入的。这个DefaultListableBeanFactory在前面已经碰到过多次,相信大家对它一定不会感到陌生。在开始分析之前,先回到IoC容器的初始化入口,也就是看一下refresh方法。这个方法的最初是在FileSystemXmlApplicationContext的构造函数中被调用的,它的调用标志着容器初始化的开始,这些初始化对象就是BeanDefinition数据,初始化入口如代码清单2-7所示。

代码清单2-7 启动BeanDefinition的载入

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {super(parent);this.setConfigLocations(configLocations);if (refresh) {//这里调用容器的refresh,是载入BeanDefinition的入口this.refresh();}}

​ 对容器的启动来说,refresh是一个很重要的方法,下面介绍一下它的实现。该方法在AbstractApplicationContext类(它是FileSystemXmlApplicationContext的基类)中找到,它详细地描述了整个ApplicationContext的初始化过程,比如BeanFactory的更新,MessageSource和PostProcessor的注册,等等。这里看起来更像是对ApplicationContext进行初始化的模板或执行提纲,这个执行过程为Bean的生命周期管理提供了条件。熟悉IoC容器使用的读者,从这一系列调用的名字就能大致了解应用上下文初始化的主要内容。这里就直接列出代码,不做太多的解释了。这个IoC容器的refresh过程如代码清单2-8所示。

代码清单2-8 对IoC容器执行refresh的过程

public void refresh() throws BeansException, IllegalStateException {// 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛synchronized(this.startupShutdownMonitor) {// 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符this.prepareRefresh();// 这步比较关键,这步首先刷新BeanFactory,关闭之前的bean factory,然后初始化新的bean factory, loadBeanDefinitions来<font color=red>载入BeanDefinition</font>//完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中,// 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,// 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean// 这块待会会展开说this.prepareBeanFactory(beanFactory);try {//设置BeanFactoy的后置处理// 【这里需要知道 BeanFactoryPostProcessor 这个知识点,Bean 如果实现了此接口,// 那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。】// 这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化// 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事this.postProcessBeanFactory(beanFactory);//调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的// 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法this.invokeBeanFactoryPostProcessors(beanFactory);// 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别// 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化this.registerBeanPostProcessors(beanFactory);// 初始化当前 ApplicationContext 的 MessageSource,国际化这里就不展开说了,不然没完没了了this.initMessageSource();// 初始化当前 ApplicationContext 的事件广播器,这里也不展开了this.initApplicationEventMulticaster();// 从方法名就可以知道,典型的模板方法(钩子方法),// 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前)this.onRefresh();// 注册事件监听器,监听器需要实现 ApplicationListener 接口。这也不是我们的重点,过this.registerListeners();// 重点,重点,重点// 初始化所有的 singleton beans//(lazy-init 的除外)this.finishBeanFactoryInitialization(beanFactory);// 最后,广播事件,ApplicationContext 初始化完成this.finishRefresh();} catch (BeansException var9) {if (this.logger.isWarnEnabled()) {this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);}// Destroy already created singletons to avoid dangling resources.// 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源this.destroyBeans();// 重置'active'标志this.cancelRefresh(var9);throw var9;} finally {this.resetCommonCaches();}}
}

下面,我们开始一步步来肢解这个 refresh() 方法。

1. 创建 Bean 容器前的准备工作

这个比较简单,直接看代码中的几个注释即可。

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 environmentinitPropertySources();// 校验 xml 配置文件getEnvironment().validateRequiredProperties();this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

2. 创建 Bean 容器,加载并注册 Bean

我们回到 refresh() 方法中的下一行 obtainFreshBeanFactory()。

注意,这个方法是全文最重要的部分之一,这里将会初始化 BeanFactory、加载 Bean、注册 Bean 等等。

当然,这步结束后,Bean 并没有完成初始化。这里指的是 Bean 实例并未在这一步生成。

// AbstractApplicationContext.java

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

// AbstractRefreshableApplicationContext.java 124

protected final void refreshBeanFactory() throws BeansException {// 如果 ApplicationContext 中已经加载过 BeanFactory 了,销毁所有 Bean,关闭 BeanFactory// 注意,应用中 BeanFactory 本来就是可以多个的,这里可不是说应用全局是否有 BeanFactory,而是当前// ApplicationContext 是否有 BeanFactoryif (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {// 初始化一个 DefaultListableBeanFactory,为什么用这个,我们马上说。DefaultListableBeanFactory beanFactory = createBeanFactory();// 用于 BeanFactory 的序列化,我想不部分人应该都用不到beanFactory.setSerializationId(getId());// 下面这两个方法很重要,别跟丢了,具体细节之后说// 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用customizeBeanFactory(beanFactory);// 加载 Bean 到 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);}
}

// AbstractRefreshableApplicationContext.java 205

protected DefaultListableBeanFactory createBeanFactory() {return new DefaultListableBeanFactory(getInternalParentBeanFactory());}

看到这里的时候,我觉得读者就应该站在高处看 ApplicationContext 了,ApplicationContext 继承自 BeanFactory,但是它不应该被理解为 BeanFactory 的实现类,而是说其内部持有一个实例化的 BeanFactory(DefaultListableBeanFactory)。以后所有的 BeanFactory 相关的操作其实是委托给这个实例来处理的。

我们说说为什么选择实例化 DefaultListableBeanFactory ?前面我们说了有个很重要的接口 ConfigurableListableBeanFactory,它实现了 BeanFactory 下面一层的所有三个接口,我把之前的继承图再拿过来大家再仔细看一下:

​ 我们可以看到 ConfigurableListableBeanFactory 只有一个实现类 DefaultListableBeanFactory,而且实现类 DefaultListableBeanFactory 还通过实现右边的 AbstractAutowireCapableBeanFactory 通吃了右路。所以结论就是,最底下这个家伙 DefaultListableBeanFactory 基本上是最牛的 BeanFactory 了,这也是为什么这边会使用这个类来实例化的原因。

如果你想要在程序运行的时候动态往 Spring IOC 容器注册新的 bean,就会使用到这个类。那我们怎么在运行时获得这个实例呢?

之前我们说过 ApplicationContext 接口能获取到 AutowireCapableBeanFactory,就是最右上角那个,然后它向下转型就能得到 DefaultListableBeanFactory 了。

那怎么拿到 ApplicationContext 实例呢?如果你不会,说明你没用过 Spring。

在继续往下之前,我们需要先了解 BeanDefinition。我们说 BeanFactory 是 Bean 容器,那么 Bean 又是什么呢?

这里的 BeanDefinition 就是我们所说的 Spring 的 Bean,我们自己定义的各个 Bean 其实会转换成一个个 BeanDefinition 存在于 Spring 的 BeanFactory 中。

所以,如果有人问你 Bean 是什么的时候,你要知道 Bean 在代码层面上可以简单认为是 BeanDefinition 的实例。

BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {// 我们可以看到,默认只提供 sington 和 prototype 两种,// 很多读者可能知道还有 request, session, globalSession, application, websocket 这几种,// 不过,它们属于基于 web 的扩展。String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;// 比较不重要,直接跳过吧int ROLE_APPLICATION = 0;int ROLE_SUPPORT = 1;int ROLE_INFRASTRUCTURE = 2;// 设置父 Bean,这里涉及到 bean 继承,不是 java 继承。请参见附录的详细介绍// 一句话就是:继承父 Bean 的配置信息而已void setParentName(String parentName);// 获取父 BeanString getParentName();// 设置 Bean 的类名称,将来是要通过反射来生成实例的void setBeanClassName(String beanClassName);// 获取 Bean 的类名称String getBeanClassName();// 设置 bean 的 scopevoid setScope(String scope);String getScope();// 设置是否懒加载void setLazyInit(boolean lazyInit);boolean isLazyInit();// 设置该 Bean 依赖的所有的 Bean,注意,这里的依赖不是指属性依赖(如 @Autowire 标记的),// 是 depends-on="" 属性设置的值。void setDependsOn(String... dependsOn);// 返回该 Bean 的所有依赖String[] getDependsOn();// 设置该 Bean 是否可以注入到其他 Bean 中,只对根据类型注入有效,// 如果根据名称注入,即使这边设置了 false,也是可以的void setAutowireCandidate(boolean autowireCandidate);// 该 Bean 是否可以注入到其他 Bean 中boolean isAutowireCandidate();// 主要的。同一接口的多个实现,如果不指定名字的话,Spring 会优先选择设置 primary 为 true 的 beanvoid setPrimary(boolean primary);// 是否是 primary 的boolean isPrimary();// 如果该 Bean 采用工厂方法生成,指定工厂名称。对工厂不熟悉的读者,请参加附录// 一句话就是:有些实例不是用反射生成的,而是用工厂模式生成的void setFactoryBeanName(String factoryBeanName);// 获取工厂名称String getFactoryBeanName();// 指定工厂类中的 工厂方法名称void setFactoryMethodName(String factoryMethodName);// 获取工厂类中的 工厂方法名称String getFactoryMethodName();// 构造器参数ConstructorArgumentValues getConstructorArgumentValues();// Bean 中的属性值,后面给 bean 注入属性值的时候会说到MutablePropertyValues getPropertyValues();// 是否 singletonboolean isSingleton();// 是否 prototypeboolean isPrototype();// 如果这个 Bean 是被设置为 abstract,那么不能实例化,// 常用于作为 父bean 用于继承,其实也很少用......boolean isAbstract();int getRole();String getDescription();String getResourceDescription();BeanDefinition getOriginatingBeanDefinition();
}

这个 BeanDefinition 其实已经包含很多的信息了,暂时不清楚所有的方法对应什么东西没关系,希望看完本文后读者可以彻底搞清楚里面的所有东西。
这里接口虽然那么多,但是没有类似 getInstance() 这种方法来获取我们定义的类的实例,真正的我们定义的类生成的实例到哪里去了呢?别着急,这个要很后面才能讲到。

有了 BeanDefinition 的概念以后,我们再往下看 refreshBeanFactory() 方法中的剩余部分:

customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);

虽然只有两个方法,但路还很长啊。。。

customizeBeanFactory

customizeBeanFactory(beanFactory) 比较简单,就是配置是否允许 BeanDefinition 覆盖、是否允许循环引用。

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {if (this.allowBeanDefinitionOverriding != null) {// 是否允许 Bean 定义覆盖beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.allowCircularReferences != null) {// 是否允许 Bean 间的循环依赖beanFactory.setAllowCircularReferences(this.allowCircularReferences);}
}

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

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

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

至于这两个属性怎么配置?我在附录中进行了介绍,尤其对于覆盖问题,很多人都希望禁止出现 Bean 覆盖,可是 Spring 默认是不同文件的时候可以覆盖的。

之后的源码中还会出现这两个属性,读者有个印象就可以了,它们不是非常重要

加载 Bean: loadBeanDefinitions

接下来是最重要的 loadBeanDefinitions(beanFactory) 方法了,这个方法将根据配置,加载各个 Bean,然后放到 BeanFactory 中。

读取配置的操作在 XmlBeanDefinitionReader 中,其负责加载配置、解析。

// AbstractXmlApplicationContext.java 80

/** 我们可以看到,此方法将通过一个 XmlBeanDefinitionReader 实例来加载各个 Bean。*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// 给这个 BeanFactory 实例化一个 XmlBeanDefinitionReaderXmlBeanDefinitionReader 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));// 初始化 BeanDefinitionReader,其实这个是提供给子类覆写的,// 我看了一下,没有类覆写这个方法,我们姑且当做不重要吧initBeanDefinitionReader(beanDefinitionReader);// 重点来了,继续往下loadBeanDefinitions(beanDefinitionReader);
}

现在还在这个类中,接下来用刚刚初始化的 Reader 开始来加载 xml 配置,这块代码读者可以选择性跳过,不是很重要。也就是说,下面这个代码块,读者可以很轻松地略过。

// AbstractXmlApplicationContext.java 120

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {Resource[] configResources = getConfigResources();if (configResources != null) {// 往下看reader.loadBeanDefinitions(configResources);}String[] configLocations = getConfigLocations();if (configLocations != null) {// 2reader.loadBeanDefinitions(configLocations);}
}// 上面虽然有两个分支,不过第二个分支很快通过解析路径转换为 Resource 以后也会进到这里
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {Assert.notNull(resources, "Resource array must not be null");int counter = 0;// 注意这里是个 for 循环,也就是每个文件是一个 resourcefor (Resource resource : resources) {// 继续往下看counter += loadBeanDefinitions(resource);}// 最后返回 counter,表示总共加载了多少的 BeanDefinitionreturn counter;
}// XmlBeanDefinitionReader 303
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions(new EncodedResource(resource));
}// XmlBeanDefinitionReader 314
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {Assert.notNull(encodedResource, "EncodedResource must not be null");if (logger.isInfoEnabled()) {logger.info("Loading XML bean definitions from " + encodedResource.getResource());}// 用一个 ThreadLocal 来存放配置文件资源Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet<EncodedResource>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {InputStream inputStream = encodedResource.getResource().getInputStream();try {InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}// 核心部分是这里,往下面看return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}finally {inputStream.close();}}catch (IOException ex) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);}finally {currentResources.remove(encodedResource);if (currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}
}// 还在这个文件中,第 388 行
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {// 这里就不看了,将 xml 文件转换为 Document 对象Document doc = doLoadDocument(inputSource, resource);// 继续return registerBeanDefinitions(doc, resource);}catch (...
}
// 还在这个文件中,第 505 行
// 返回值:返回从当前配置文件加载了多少数量的 Bean
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();int countBefore = getRegistry().getBeanDefinitionCount();// 这里documentReader.registerBeanDefinitions(doc, createReaderContext(resource));return getRegistry().getBeanDefinitionCount() - countBefore;
}
// DefaultBeanDefinitionDocumentReader 90
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;logger.debug("Loading bean definitions");Element root = doc.getDocumentElement();// 从 xml 根节点开始解析文件doRegisterBeanDefinitions(root);
}

经过漫长的链路,一个配置文件终于转换为一颗 DOM 树了,注意,这里指的是其中一个配置文件,不是所有的,读者可以看到上面有个 for 循环的。下面开始从根节点开始解析:

doRegisterBeanDefinitions:

// DefaultBeanDefinitionDocumentReader 116
protected void doRegisterBeanDefinitions(Element root) {// 我们看名字就知道,BeanDefinitionParserDelegate 必定是一个重要的类,它负责解析 Bean 定义,// 这里为什么要定义一个 parent? 看到后面就知道了,是递归问题,// 因为 <beans /> 内部是可以定义 <beans /> 的,所以这个方法的 root 其实不一定就是 xml 的根节点,也可以是嵌套在里面的 <beans /> 节点,从源码分析的角度,我们当做根节点就好了BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {// 这块说的是根节点 <beans ... profile="dev" /> 中的 profile 是否是当前环境需要的,// 如果当前环境配置的 profile 不包含此 profile,那就直接 return 了,不对此 <beans /> 解析// 不熟悉 profile 为何物,不熟悉怎么配置 profile 读者的请移步附录区String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isInfoEnabled()) {logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +"] not matching: " + getReaderContext().getResource());}return;}}}preProcessXml(root); // 钩子// 往下看parseBeanDefinitions(root, this.delegate);postProcessXml(root); // 钩子this.delegate = parent;
}

preProcessXml(root) 和 postProcessXml(root) 是给子类用的钩子方法,鉴于没有被使用到,也不是我们的重点,我们直接跳过。

这里涉及到了 profile 的问题,对于不了解的读者,我在附录中对 profile 做了简单的解释,读者可以参考一下。

接下来,看核心解析方法 parseBeanDefinitions(root, this.delegate) :

// default namespace 涉及到的就四个标签 <import />、<alias />、<bean /> 和 <beans />,
// 其他的属于 custom 的
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {if (delegate.isDefaultNamespace(root)) {NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element) {Element ele = (Element) node;if (delegate.isDefaultNamespace(ele)) {// 解析 default namespace 下面的几个元素parseDefaultElement(ele, delegate);}else {// 解析其他 namespace 的元素delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}
}

从上面的代码,我们可以看到,对于每个配置来说,分别进入到 parseDefaultElement(ele, delegate); 和 delegate.parseCustomElement(ele); 这两个分支了。

parseDefaultElement(ele, delegate) 代表解析的节点是 <import /><alias /><bean /><beans /> 这几个。

这里的四个标签之所以是 default 的,是因为它们是处于这个 namespace 下定义的:

http://www.springframework.org/schema/beans

又到初学者科普时间,不熟悉 namespace 的读者请看下面贴出来的 xml,这里的第二行 xmlns 就是咯。

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"default-autowire="byName">

而对于其他的标签,将进入到 delegate.parseCustomElement(element) 这个分支。如我们经常会使用到的 <mvc /><task /><context /><aop />等。

这些属于扩展,如果需要使用上面这些 ”非 default“ 标签,那么上面的 xml 头部的地方也要引入相应的 namespace 和 .xsd 文件的路径,如下所示。同时代码中需要提供相应的 parser 来解析,如 MvcNamespaceHandler、TaskNamespaceHandler、ContextNamespaceHandler、AopNamespaceHandler 等。

假如读者想分析 <context:property-placeholder location="classpath:xx.properties" /> 的实现原理,就应该到 ContextNamespaceHandler 中找答案。

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvc   http://www.springframework.org/schema/mvc/spring-mvc.xsd  "
default-autowire="byName">

同理,以后你要是碰到 <dubbo /> 这种标签,那么就应该搜一搜是不是有 DubboNamespaceHandler 这个处理类。

如果每个标签都说,那我不吐血,你们都要吐血了。我们挑我们的重点 <bean /> 标签出来说。

processBeanDefinition 解析 bean 标签

下面是 processBeanDefinition 解析 <bean /> 标签:

// DefaultBeanDefinitionDocumentReader 298

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {// 将 <bean /> 节点中的信息提取出来,然后封装到一个 BeanDefinitionHolder 中,细节往下看BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);// 下面的几行先不要看,跳过先,跳过先,跳过先,后面会继续说的if (bdHolder != null) {bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// Register the final decorated instance.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));}
}

继续往下看怎么解析之前,我们先看下 <bean /> 标签中可以定义哪些属性:

Property
class 类的全限定名
name 可指定 id、name(用逗号、分号、空格分隔)
scope 作用域
constructor arguments 指定构造参数
properties 设置属性的值
autowiring mode no(默认值)、byName、byType、 constructor
lazy-initialization mode 是否懒加载(如果被非懒加载的bean依赖了那么其实也就不能懒加载了)
initialization method bean 属性设置完成后,会调用这个方法
destruction method bean 销毁后的回调方法

上面表格中的内容我想大家都非常熟悉吧,如果不熟悉,那就是你不够了解 Spring 的配置了。

简单地说就是像下面这样子:

<bean id="exampleBean" name="name1, name2, name3" class="com.javadoop.ExampleBean"scope="singleton" lazy-init="true" init-method="init" destroy-method="cleanup"><!-- 可以用下面三种形式指定构造参数 --><constructor-arg type="int" value="7500000"/><constructor-arg name="years" value="7500000"/><constructor-arg index="0" value="7500000"/><!-- property 的几种情况 --><property name="beanOne"><ref bean="anotherExampleBean"/></property><property name="beanTwo" ref="yetAnotherBean"/><property name="integerProperty" value="1"/>
</bean>

当然,除了上面举例出来的这些,还有 factory-bean、factory-method、<lockup-method /><replaced-method /><meta /><qualifier /> 这几个,大家是不是熟悉呢?自己检验一下自己对 Spring 中 bean 的了解程度。

有了以上这些知识以后,我们再继续往里看怎么解析 bean 元素,是怎么转换到 BeanDefinitionHolder 的。

我们回到解析 <bean /> 的入口方法:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {// 将 <bean /> 节点转换为 BeanDefinitionHolder,就是上面说的一堆BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {// 如果有自定义属性的话,进行相应的解析,先忽略bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// 我们把这步叫做 注册Bean 吧BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// 注册完成后,发送事件,本文不展开说这个getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}
}

大家再仔细看一下这块吧,我们后面就不回来说这个了。这里已经根据一个 <bean /> 标签产生了一个 BeanDefinitionHolder 的实例,这个实例里面也就是一个 BeanDefinition 的实例和它的 beanName、aliases 这三个信息,注意,我们的关注点始终在 BeanDefinition 上:

public class BeanDefinitionHolder implements BeanMetadataElement {private final BeanDefinition beanDefinition;private final String beanName;private final String[] aliases;
...

然后我们准备注册这个 BeanDefinition,最后,把这个注册事件发送出去。

下面,我们开始说说注册 Bean 吧。

注册 Bean

// BeanDefinitionReaderUtils 143

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {String beanName = definitionHolder.getBeanName();// 注册这个 Beanregistry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// 如果还有别名的话,也要根据别名全部注册一遍,不然根据别名就会找不到 Bean 了String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {// alias -> beanName 保存它们的别名信息,这个很简单,用一个 map 保存一下就可以了,// 获取的时候,会先将 alias 转换为 beanName,然后再查找registry.registerAlias(beanName, alias);}}
}

别名注册的放一边,毕竟它很简单,我们看看怎么注册 Bean。

// DefaultListableBeanFactory 793

@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(...);}}// old? 还记得 “允许 bean 覆盖” 这个配置吗?allowBeanDefinitionOverridingBeanDefinition oldBeanDefinition;// 之后会看到,所有的 Bean 注册后会放入这个 beanDefinitionMap 中oldBeanDefinition = this.beanDefinitionMap.get(beanName);// 处理重复名称的 Bean 定义的情况if (oldBeanDefinition != null) {if (!isAllowBeanDefinitionOverriding()) {// 如果不允许覆盖的话,抛异常throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription()...}else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {// log...用框架定义的 Bean 覆盖用户自定义的 Bean}else if (!beanDefinition.equals(oldBeanDefinition)) {// log...用新的 Bean 覆盖旧的 Bean}else {// log...用同等的 Bean 覆盖旧的 Bean,这里指的是 equals 方法返回 true 的 Bean}// 覆盖this.beanDefinitionMap.put(beanName, beanDefinition);}else {// 判断是否已经有其他的 Bean 开始初始化了.// 注意,"注册Bean" 这个动作结束,Bean 依然还没有初始化,我们后面会有大篇幅说初始化过程,// 在 Spring 容器启动的最后,会 预初始化 所有的 singleton beansif (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)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 {// 最正常的应该是进到这个分支。// 将 BeanDefinition 放到这个 map 中,这个 map 保存了所有的 BeanDefinitionthis.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);}
}

总结一下,到这里已经初始化了 Bean 容器,<bean /> 配置也相应的转换为了一个个 BeanDefinition,然后注册了各个 BeanDefinition 到注册中心,并且发送了注册事件。

--------- 分割线 ---------

到这里是一个分水岭,前面的内容都还算比较简单,不过应该也比较繁琐,大家要清楚地知道前面都做了哪些事情。

Bean 容器实例化完成后

说到这里,我们回到 refresh() 方法,我重新贴了一遍代码,看看我们说到哪了。是的,我们才说完 obtainFreshBeanFactory() 方法。

考虑到篇幅,这里开始大幅缩减掉没必要详细介绍的部分,大家直接看下面的代码中的注释就好了。

@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 初始化之前和初始化之后得到执行。这里仅仅是注册,之后会看到回调这两方法的时机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();}}
}

准备 Bean 容器: prepareBeanFactory

之前我们说过,Spring 把我们在 xml 配置的 bean 都注册以后,会"手动"注册一些特殊的 bean。

这里简单介绍下 prepareBeanFactory(factory) 方法:

// 设置 BeanFactory 的类加载器,我们知道 BeanFactory 需要加载类,也就需要类加载器,// 这里设置为加载当前 ApplicationContext 类的类加载器beanFactory.setBeanClassLoader(getClassLoader());// 设置 BeanExpressionResolverbeanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));//beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));// 添加一个 BeanPostProcessor,这个 processor 比较简单:// 实现了 Aware 接口的 beans 在初始化的时候,这个 processor 负责回调,// 这个我们很常用,如我们会为了获取 ApplicationContext 而 implement ApplicationContextAware// 注意:它不仅仅回调 ApplicationContextAware,//   还会负责回调 EnvironmentAware、ResourceLoaderAware 等,看下源码就清楚了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);/*** 下面几行就是为特殊的几个 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);// 这个 BeanPostProcessor 也很简单,在 bean 实例化后,如果是 ApplicationListener 的子类,// 那么将其添加到 listener 列表中,可以理解成:注册 事件监听器beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));// 这里涉及到特殊的 bean,名为:loadTimeWeaver,这不是我们的重点,忽略它// tips: ltw 是 AspectJ 的概念,指的是在运行期进行织入,这个和 Spring AOP 不一样,//    感兴趣的读者请参考我写的关于 AspectJ 的另一篇文章 https://www.javadoop.com/post/aspectjif (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()));}/*** 从下面几行代码我们可以知道,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());}
}

在上面这块代码中,Spring 对一些特殊的 bean 进行了处理,读者如果暂时还不能消化它们也没有关系,慢慢往下看。

getBean

在继续前进之前,读者应该具备 FactoryBean 的知识,如果读者还不熟悉,请移步附录部分了解 FactoryBean。

// AbstractBeanFactory 196

@Override
public Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);
}// 我们在剖析初始化 Bean 的过程,但是 getBean 方法我们经常是用来从容器中获取 Bean 用的,注意切换思路,
// 已经初始化过了就从容器中直接返回,否则就先初始化再返回
@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {// 获取一个 “正统的” beanName,处理两种情况,一个是前面说的 FactoryBean(前面带 ‘&’),// 一个是别名问题,因为这个方法是 getBean,获取 Bean 用的,你要是传一个别名进来,是完全可以的final String beanName = transformedBeanName(name);// 注意跟着这个,这个是返回值Object bean; // 检查下是不是已经创建过了Object sharedInstance = getSingleton(beanName);// 这里说下 args 呗,虽然看上去一点不重要。前面我们一路进来的时候都是 getBean(beanName),// 所以 args 传参其实是 null 的,但是如果 args 不为空的时候,那么意味着调用方不是希望获取 Bean,而是创建 Beanif (sharedInstance != null && args == null) {if (logger.isDebugEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.debug("...");}else {logger.debug("Returning cached instance of singleton bean '" + beanName + "'");}}// 下面这个方法:如果是普通 Bean 的话,直接返回 sharedInstance,// 如果是 FactoryBean 的话,返回它创建的那个实例对象// (FactoryBean 知识,读者若不清楚请移步附录)bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {if (isPrototypeCurrentlyInCreation(beanName)) {// 创建过了此 beanName 的 prototype 类型的 bean,那么抛异常,// 往往是因为陷入了循环引用throw new BeanCurrentlyInCreationException(beanName);}// 检查一下这个 BeanDefinition 在容器中是否存在BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// 如果当前容器不存在这个 BeanDefinition,试试父容器中有没有String nameToLookup = originalBeanName(name);if (args != null) {// 返回父容器的查询结果return (T) parentBeanFactory.getBean(nameToLookup, args);}else {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}}if (!typeCheckOnly) {// typeCheckOnly 为 false,将当前 beanName 放入一个 alreadyCreated 的 Set 集合中。markBeanAsCreated(beanName);}/** 稍稍总结一下:* 到这里的话,要准备创建 Bean 了,对于 singleton 的 Bean 来说,容器中还没创建过此 Bean;* 对于 prototype 的 Bean 来说,本来就是要创建一个新的 Bean。*/try {final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// 先初始化依赖的所有 Bean,这个很好理解。// 注意,这里的依赖指的是 depends-on 中定义的依赖String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {// 检查是不是有循环依赖,这里的循环依赖和我们前面说的循环依赖又不一样,这里肯定是不允许出现的,不然要乱套了,读者想一下就知道了if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 注册一下依赖关系registerDependentBean(dep, beanName);// 先初始化被依赖项getBean(dep);}}// 如果是 singleton scope 的,创建 singleton 的实例if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {try {// 执行创建 Bean,详情后面再说return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 如果是 prototype scope 的,创建 prototype 的实例else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);// 执行创建 BeanprototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}// 如果不是 singleton 和 prototype 的话,需要委托给相应的实现类来处理else {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {beforePrototypeCreation(beanName);try {// 执行创建 Beanreturn createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// 最后,检查一下类型对不对,不对的话就抛异常,对的话就返回了if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {try {return getTypeConverter().convertIfNecessary(bean, requiredType);}catch (TypeMismatchException ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;
}

第三个参数 args 数组代表创建实例需要的参数,不就是给构造方法用的参数,或者是工厂 Bean 的参数嘛,不过要注意,在我们的初始化阶段,args 是 null。

这回我们要到一个新的类了 AbstractAutowireCapableBeanFactory,看类名,AutowireCapable?类名是不是也说明了点问题了。

主要是为了以下场景,采用 @Autowired 注解注入属性值:

public class MessageServiceImpl implements MessageService {@Autowiredprivate UserService userService;public String getMessage() {return userService.getMessage();}
}
<bean id="messageService" class="com.javadoop.example.MessageServiceImpl" />

以上这种属于混用了 xml 和 注解 两种方式的配置方式,Spring 会处理这种情况。

好了,读者要知道这么回事就可以了,继续向前。

 * Central method of this class: creates a bean instance,* populates the bean instance, applies post-processors, etc.* @see #doCreateBean*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {if (logger.isDebugEnabled()) {logger.debug("Creating instance of bean '" + beanName + "'");}RootBeanDefinition mbdToUse = mbd;// 确保 BeanDefinition 中的 Class 被加载Class<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}// 准备方法覆写,这里又涉及到一个概念:MethodOverrides,它来自于 bean 定义中的 <lookup-method />// 和 <replaced-method />,如果读者感兴趣,回到 bean 解析的地方看看对这两个标签的解析。// 我在附录中也对这两个标签的相关知识点进行了介绍,读者可以移步去看看try {mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}try {// 让 InstantiationAwareBeanPostProcessor 在这一步有机会返回代理,// 在 《Spring AOP 源码分析》那篇文章中有解释,这里先跳过Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean; }}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}// 重头戏,创建 beanObject beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isDebugEnabled()) {logger.debug("Finished creating instance of bean '" + beanName + "'");}return beanInstance;
}

创建 Bean

我们继续往里看 doCreateBean 这个方法:

/*** Actually create the specified bean. Pre-creation processing has already happened* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.* <p>Differentiates between default bean instantiation, use of a* factory method, and autowiring a constructor.* @param beanName the name of the bean* @param mbd the merged bean definition for the bean* @param args explicit arguments to use for constructor or factory method invocation* @return a new instance of the bean* @throws BeanCreationException if the bean could not be created* @see #instantiateBean* @see #instantiateUsingFactoryMethod* @see #autowireConstructor*/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {// 说明不是 FactoryBean,这里实例化 Bean,这里非常关键,细节之后再说instanceWrapper = createBeanInstance(beanName, mbd, args);}// 这个就是 Bean 里面的 我们定义的类 的实例,很多地方我直接描述成 "bean 实例"final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);// 类型Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);mbd.resolvedTargetType = beanType;// 建议跳过吧,涉及接口:MergedBeanDefinitionPostProcessorsynchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {// MergedBeanDefinitionPostProcessor,这个我真不展开说了,直接跳过吧,很少用的applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.// 下面这块代码是为了解决循环依赖的问题,以后有时间,我再对循环依赖这个问题进行解析吧boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {return getEarlyBeanReference(beanName, mbd, bean);}});}// Initialize the bean instance.Object exposedObject = bean;try {// 这一步也是非常关键的,这一步负责属性装配,因为前面的实例只是实例化了,并没有设值,这里就是设值populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {// 还记得 init-method 吗?还有 InitializingBean 接口?还有 BeanPostProcessor 接口?// 这里就是处理 bean 初始化完成后的各种回调exposedObject = initializeBean(beanName, exposedObject, mbd);}}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {//Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;
}

到这里,我们已经分析完了 doCreateBean 方法,总的来说,我们已经说完了整个初始化流程。

接下来我们挑 doCreateBean 中的三个细节出来说说。一个是创建 Bean 实例的 createBeanInstance 方法,一个是依赖注入的 populateBean 方法,还有就是回调方法 initializeBean。

注意了,接下来的这三个方法要认真说那也是极其复杂的,很多地方我就点到为止了,感兴趣的读者可以自己往里看,最好就是碰到不懂的,自己写代码去调试它。

创建 Bean 实例

我们先看看 createBeanInstance 方法。需要说明的是,这个方法如果每个分支都分析下去,必然也是极其复杂冗长的,我们挑重点说。此方法的目的就是实例化我们指定的类。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {// 确保已经加载了此 classClass<?> beanClass = resolveBeanClass(mbd, beanName);// 校验一下这个类的访问权限if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());}if (mbd.getFactoryMethodName() != null)  {// 采用工厂方法实例化,不熟悉这个概念的读者请看附录,注意,不是 FactoryBeanreturn instantiateUsingFactoryMethod(beanName, mbd, args);}// 如果不是第一次创建,比如第二次创建 prototype bean。// 这种情况下,我们可以从第一次创建知道,采用无参构造函数,还是构造函数依赖注入 来完成实例化boolean resolved = false;boolean autowireNecessary = false;if (args == null) {synchronized (mbd.constructorArgumentLock) {if (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}if (resolved) {if (autowireNecessary) {// 构造函数依赖注入return autowireConstructor(beanName, mbd, null, null);}else {// 无参构造函数return instantiateBean(beanName, mbd);}}// 判断是否采用有参构造函数Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if (ctors != null ||mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {// 构造函数依赖注入return autowireConstructor(beanName, mbd, ctors, args);}// 调用无参构造函数return instantiateBean(beanName, mbd);
}

挑个简单的无参构造函数构造实例来看看:

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {try {Object beanInstance;final BeanFactory parent = this;if (System.getSecurityManager() != null) {beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {@Overridepublic Object run() {return getInstantiationStrategy().instantiate(mbd, beanName, parent);}}, getAccessControlContext());}else {// 实例化beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);}// 包装一下,返回BeanWrapper bw = new BeanWrapperImpl(beanInstance);initBeanWrapper(bw);return bw;}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);}
}
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);

bean 属性注入

看完了 createBeanInstance(…) 方法,我们来看看 populateBean(…) 方法,该方法负责进行属性设值,处理依赖。

// AbstractAutowireCapableBeanFactory 1203

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {// bean 实例的所有属性都在这里了PropertyValues pvs = mbd.getPropertyValues();if (bw == null) {if (!pvs.isEmpty()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}else {// Skip property population phase for null instance.return;}}// 到这步的时候,bean 实例化完成(通过工厂方法或构造方法),但是还没开始属性设值,// InstantiationAwareBeanPostProcessor 的实现类可以在这里对 bean 进行状态修改,// 我也没找到有实际的使用,所以我们暂且忽略这块吧boolean continueWithPropertyPopulation = true;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;// 如果返回 false,代表不需要进行后续的属性设值,也不需要再经过其他的 BeanPostProcessor 的处理if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {continueWithPropertyPopulation = false;break;}}}}if (!continueWithPropertyPopulation) {return;}if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// 通过名字找到所有属性值,如果是 bean 依赖,先初始化依赖的 bean。记录依赖关系if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// 通过类型装配。复杂一些if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);if (hasInstAwareBpps || needsDepCheck) {PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);if (hasInstAwareBpps) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;// 这里有个非常有用的 BeanPostProcessor 进到这里: AutowiredAnnotationBeanPostProcessor// 对采用 @Autowired、@Value 注解的依赖进行设值,这里的内容也是非常丰富的,不过本文不会展开说了,感兴趣的读者请自行研究pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvs == null) {return;}}}}if (needsDepCheck) {checkDependencies(beanName, mbd, filteredPds, pvs);}}// 设置 bean 实例的属性值applyPropertyValues(beanName, mbd, bw, pvs);
}
initializeBean

属性注入完成后,这一步其实就是处理各种回调了,这块代码比较简单。

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {if (System.getSecurityManager() != null) {AccessController.doPrivileged(new PrivilegedAction<Object>() {@Overridepublic Object run() {invokeAwareMethods(beanName, bean);return null;}}, getAccessControlContext());}else {// 如果 bean 实现了 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 接口,回调invokeAwareMethods(beanName, bean);}Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {// BeanPostProcessor 的 postProcessBeforeInitialization 回调wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {// 处理 bean 中定义的 init-method,// 或者如果 bean 实现了 InitializingBean 接口,调用 afterPropertiesSet() 方法invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}if (mbd == null || !mbd.isSynthetic()) {// BeanPostProcessor 的 postProcessAfterInitialization 回调wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;
}

大家发现没有,BeanPostProcessor 的两个回调都发生在这边,只不过中间处理了 init-method,是不是和读者原来的认知有点不一样了?

7.3 BeanDefinition在IoC容器中的注册

​ 前面已经分析过BeanDefinition在IoC容器中载入和解析的过程。在这些动作完成以后,用户定义的BeanDefinition信息已经在IoC容器内建立起了自己的数据结构以及相应的数据表示,但此时这些数据还不能供IoC容器直接使用,需要在IoC容器中对这些BeanDefinition数据进行注册。这个注册为IoC容器提供了更友好的使用方式,在DefaultListableBeanFactory中,是通过一个HashMap来持有载入的BeanDefinition的,这个HashMap的定义在DefaultListableBeanFactory中可以看到,如下所示。

/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = newConcurrentHashMap<String, BeanDefinition>();

​ 将解析得到的BeanDefinition向IoC容器中的beanDefinitionMap注册的过程是在载入BeanDefinition完成后进行的,注册的调用过程如图2-12所示。

从源代码实现的角度,可以看到相关的调用关系如图2-13所示。

图2-13 registerBeanDefinition的调用关系

​ 我们跟踪以上的代码调用去看一下具体的注册实现,在DefaultListableBeanFactory中实现了BeanDefinitionRegistry的接口,这个接口的实现完成BeanDefinition向容器的注册。这个注册过程不复杂,就是把解析得到的BeanDefinition设置到hashMap中去。需要注意的是,如果遇到同名的BeanDefinition,进行处理的时候需要依据allowBeanDefinitionOverriding的配置来完成。具体的实现如代码清单2-21所示。

​ 完成了BeanDefinition的注册,就完成了IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,它们都在beanDefinitionMap里被检索和使用。容器的作用就是对这些信息进行处理和维护。这些信息是容器建立依赖反转的基础,有了这些基础数据,下面我们看一下在IoC容器中,依赖注入是怎样完成的。

2.4 IoC容器的依赖注入

上面对IoC容器的初始化过程进行了详细的分析,这个初始化过程完成的主要工作是在IoC容器中建立BeanDefinition数据映射。在此过程中并没有看到IoC容器对Bean依赖关系进行注入,接下来分析一下IoC容器是怎样对Bean的依赖关系进行注入的。
假设当前IoC容器已经载入了用户定义的Bean信息,开始分析依赖注入的原理。首先,注意到依赖注入的过程是用户第一次向IoC容器索要Bean时触发的,当然也有例外,也就是我们可以在BeanDefinition信息中通过控制lazy-init属性来让容器完成对Bean的预实例化。这个预实例化实际上也是一个完成依赖注入的过程,但它是在初始化的过程中完成的,稍后我们会详细分析这个预实例化的处理。当用户向IoC容器索要Bean时,如果读者还有印象,那么一定还记得在基本的IoC容器接口BeanFactory中,有一个getBean的接口定义,这个接口的实现就是触发依赖注入发生的地方。为了进一步了解这个依赖注入过程的实现,下面从DefaultListableBeanFactory的基类AbstractBeanFactory入手去看看getBean的实现,如代码清单2-22所示。
代码清单2-22 getBean触发的依赖注入

[Spring 深度解析]第7章 IoC容器的初始化过程相关推荐

  1. [Spring 深度解析]第3章 核心容器

    第3章 ◄核心容器► ​ 在上一章节中,我们大致了解了Spring框架,并学习了控制反转(IOC)和面向切面编程(AOP)两个重要的编程思想,这一章我们将开始学习Spring框架中的核心容器. 本章主 ...

  2. Spring IoC(二)IoC容器的初始化过程

    (一)IoC 容器初始化过程概述 1.1简要概述初始化过程 IoC 容器的初始化过程是通过refresh() 方法来启动的,这个方法标识着IoC 容器正式启动.具体来说,这个启动过程包括:BeanDe ...

  3. 《Spring技术内幕》——2.3节IoC容器的初始化过程

    2.3 IoC容器的初始化过程 简单来说,IoC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IoC容器的正式启动.具体来说,这个启动包括BeanDefinition的Res ...

  4. Spring IOC学习心得之IOC容器的初始化过程

    注:本文大多数内容都是摘自<Spring技术内幕>这本书 简单来说,Ioc容器的初始化过程是在refresh()方法中启动的,包括BeanDefinition的Resource定位,载入和 ...

  5. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  6. IOC原理之IoC容器的初始化过程

    IoC容器的初始化过程包括Resource定位.BeanDefinition的载入以及向IoC容器注册这些BeanDefinition三个阶段. IoC容器的初始化过程概要 IoC容器的初始化包括三个 ...

  7. [Spring 深度解析]第6章 Spring的IoC容器系列

    6. Spring的IoC容器系列 ​ IoC容器为开发者管理对象之间的依赖关系提供了很多便利和基础服务.有许多IoC容器供开发者选择,SpringFramework的IoC核心就是其中一个,它是开源 ...

  8. Spring IoC容器的初始化过程

    转载自:http://blog.csdn.net/u010723709/article/details/47046211 原题是:2 IOC容器初始化过程 作者:@小小旭GISer ========= ...

  9. [Spring 深度解析]第1章 Java基础

    第1章 ◄Java基础► 在学习Spring之前我们需要对Java基础语法有一定的了解,Java中最重要的两个知识点是注解和反射.注解和反射在Spring框架中应用的最广泛.掌握注解和反射,有助于后面 ...

最新文章

  1. c++数据结构队列栈尸体_一本正经的聊数据结构(3):栈和队列
  2. 「每周CV论文推荐」 初学深度学习人脸属性分析必读的文章
  3. 网络和计算机管理制度,网络和计算机使用管理制度
  4. linux 软硬连接区别---关于inode索引节点
  5. dijkstra算法学习
  6. java小球碰撞实验报告_20155317 《Java程序设计》实验五网络编程与安全实验报告...
  7. python库的学习系列之 13.2. ConfigParser — Configuration file parser
  8. 为什么选择react?为前端开发选择React的六大理由
  9. 01-C语言之父:丹尼斯·里奇
  10. 一文详细理解计算机网络 - 物理层(考试和面试必备)
  11. C语言基础之十进制与二进制转换
  12. 工程师视角的手游SDK
  13. Axure RP-产品原型设计工具使用
  14. 使用BDE数据库引擎的应用软件出现Insufficient disk space的解决方法
  15. 科思创进博会宣布筹建上海新工厂;碧迪医疗进博会签订总值达2.7亿元合作协议 | 美通社头条...
  16. 分段函数sgnx的C语言程序,几个重要的分段函数
  17. 用dnsmasq 实现DNS劫持
  18. 推荐一些常用的中外学术文献数据库网站
  19. 从0开始学习微服务(二)
  20. wangEditor 修改 “视频”菜单 的实现方式,达到上传视频的功能---完整版

热门文章

  1. 看完这部纪录片之后,我想把身上的细菌寄生虫供起来
  2. 随机森林RandomForest回归生物标记预测时间序列
  3. Genome Biology:人体各部位微生物组时间序列分析
  4. 1003个微生物基因组数据发布
  5. linux基于此语言的密码,一次有趣的Linux下.Net Core与C语言的合作开发体验:生成Linux标准的用户密码串...
  6. Python中将两个列表(list)数据zip起来
  7. pandas将dataframe中的年、月、日数据列合并成完整日期字符串、并使用to_datetime将字符串格式转化为日期格式
  8. R语言e1071包中的支持向量机:仿真数据(螺旋线性不可分数据集)、简单线性核的支持向量机SVM(模型在测试集上的表现、可视化模型预测的结果、添加超平面区域与原始数据标签进行对比分析)、如何改进核函数
  9. 使用Oracle instantClient代替Oracle Client安装
  10. R将字符串类型(Character)转化为因子类型(Factor)