注意:本版块难度很大,作为选学内容。
如果学习Spring基本内容对你来说已经非常困难了,建议跳过此小节,直接进入MVC阶段的学习,此小节会从源码角度解释Spring的整个运行原理,对初学者来说等同于小学跨越到高中,它并不是必学内容,但是对于个人开发能力的提升极为重要(推荐完成整个SSM阶段的学习并且加以实战之后再来看此部分),如果你还是觉得自己能够跟上节奏继续深入钻研底层原理,那么现在就开始吧。
首先我们大致了解一下ApplicationContext的加载流程:

我们可以看到,整个过程极为复杂,一句话肯定是无法解释的,所以我们就从ApplicationContext说起吧。
由于Spring的源码极为复杂,因此我们不可能再像了解其他框架那样直接自底向上逐行干源码了(可以自己点开看看,代码量非常之多),我们可以先从一些最基本的接口定义开始讲起,自顶向下逐步瓦解,那么我们来看看ApplicationContext最顶层接口是什么,一直往上,我们会发现有一个AbstractApplicationContext类,我们直接右键生成一个UML类图查看:
我们发现最顶层实际上是一个BeanFactory接口,那么我们就从这个接口开始研究起。
我们可以看到此接口中定义了很多的行为:
- public interface BeanFactory {
- String FACTORY_BEAN_PREFIX = "&";
-
- Object getBean(String var1) throws BeansException;
-
-
T getBean(String var1, Class var2) throws BeansException; -
- Object getBean(String var1, Object... var2) throws BeansException;
-
-
T getBean(Class var1) throws BeansException; -
-
T getBean(Class var1, Object... var2) throws BeansException; -
-
ObjectProvider getBeanProvider(Class var1) ; -
-
ObjectProvider getBeanProvider(ResolvableType var1); -
- boolean containsBean(String var1);
-
- boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
-
- boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
-
- boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
-
- boolean isTypeMatch(String var1, Class> var2) throws NoSuchBeanDefinitionException;
-
- @Nullable
- Class> getType(String var1) throws NoSuchBeanDefinitionException;
-
- @Nullable
- Class> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
-
- String[] getAliases(String var1);
- }
我们发现,其中最眼熟的就是getBean()方法了,此方法被重载了很多次,可以接受多种参数,因此,我们可以断定,一个IoC容器最基本的行为在此接口中已经被定义好了,也就是说,所有的BeanFactory实现类都应该具备容器管理Bean的基本能力,就像它的名字一样,它就是一个Bean工厂,工厂就是用来生产Bean实例对象的。
我们可以直接找到此接口的一个抽象实现AbstractBeanFactory类,它实现了getBean()方法:
- public Object getBean(String name) throws BeansException {
- return this.doGetBean(name, (Class)null, (Object[])null, false);
- }
那么我们doGetBean()接着来看方法里面干了什么:
- protected
T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { - String beanName = this.transformedBeanName(name);
- Object sharedInstance = this.getSingleton(beanName);
- Object beanInstance;
- if (sharedInstance != null && args == null) {
- ...
因为所有的Bean默认都是单例模式,对象只会存在一个,因此它会先调用父类的getSingleton()方法来直接获取单例对象,如果有的话,就可以直接拿到Bean的实例。如果没有会进入else代码块,我们接着来看,首先会进行一个判断:
- if (this.isPrototypeCurrentlyInCreation(beanName)) {
- throw new BeanCurrentlyInCreationException(beanName);
- }
这是为了解决循环依赖进行的处理,比如A和B都是以原型模式进行创建,而A中需要注入B,B中需要注入A,这时就会出现A还未创建完成,就需要B,而B这时也没创建完成,因为B需要A,而A等着B,这样就只能无限循环下去了,所以就出现了循环依赖的问题(同理,一个对象,多个对象也会出现这种情况)但是在单例模式下,由于每个Bean只会创建一个实例,Spring完全有机会处理好循环依赖的问题,只需要一个正确的赋值操作实现循环即可。那么单例模式下是如何解决循环依赖问题的呢?

我们回到getSingleton()方法中,单例模式是可以自动解决循环依赖问题的:
- @Nullable
- protected Object getSingleton(String beanName, boolean allowEarlyReference) {
- Object singletonObject = this.singletonObjects.get(beanName);
- //先从第一层列表中拿Bean实例,拿到直接返回
- if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
- //第一层拿不到,并且已经认定为处于循环状态,看看第二层有没有
- singletonObject = this.earlySingletonObjects.get(beanName);
- if (singletonObject == null && allowEarlyReference) {
- synchronized(this.singletonObjects) {
- //加锁再执行一次上述流程
- singletonObject = this.singletonObjects.get(beanName);
- if (singletonObject == null) {
- singletonObject = this.earlySingletonObjects.get(beanName);
- if (singletonObject == null) {
- //仍然没有获取到实例,只能从singletonFactory中获取了
- ObjectFactory> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
- if (singletonFactory != null) {
- singletonObject = singletonFactory.getObject();
- //丢进earlySingletonObjects中,下次就可以直接在第二层拿到了
- this.earlySingletonObjects.put(beanName, singletonObject);
- this.singletonFactories.remove(beanName);
- }
- }
- }
- }
- }
- }
-
- return singletonObject;
- }
看起来很复杂,实际上它使用了三层列表的方式来处理循环依赖的问题。包括:
当第一层拿不到时,会接着判断这个Bean是否处于创建状态isSingletonCurrentlyInCreation(),它会从一个Set集合中查询,这个集合中存储了已经创建但还未注入属性的实例对象,也就是说处于正在创建状态,如果说发现此Bean处于正在创建状态(一定是因为某个Bean需要注入这个Bean的实例),就可以断定它应该是出现了循环依赖的情况。
earlySingletonObjects相当于是专门处理循环依赖的表,一般包含singletonObjects中的全部实例,如果这个里面还是没有,接着往下走,这时会从singletonFactories中获取(所有的Bean初始化完成之后都会被丢进singletonFactories,也就是只创建了,但是还没进行依赖注入的时候)在获取到后,向earlySingletonObjects中丢入此Bean的实例,并将实例从singletonFactories中移除。
我们最后再来梳理一下流程,还是用我们刚才的例子,A依赖于B,B依赖于A:
经过整体过程梳理,关于Spring如何解决单例模式的循环依赖理解起来就非常简单了。
现在让我们回到之前的地方,原型模式下如果出现循环依赖会直接抛出异常,如果不存在会接着向下:
- //BeanFactory存在父子关系
- BeanFactory parentBeanFactory = this.getParentBeanFactory();
- //如果存在父BeanFactory,同时当前BeanFactory没有这个Bean的定义
- if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
- //这里是因为Bean可能有别名,找最原始的那个名称
- String nameToLookup = this.originalBeanName(name);
- if (parentBeanFactory instanceof AbstractBeanFactory) {
- //向父BeanFactory递归查找
- return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
- }
-
- if (args != null) {
- //根据参数查找
- return parentBeanFactory.getBean(nameToLookup, args);
- }
-
- if (requiredType != null) {
- //根据类型查找
- return parentBeanFactory.getBean(nameToLookup, requiredType);
- }
-
- //各种找
- return parentBeanFactory.getBean(nameToLookup);
- }
也就是说,BeanFactory会先看当前是否存在Bean的定义,如果没有会直接用父BeanFactory各种找。这里出现了一个新的接口BeanDefinition,既然工厂需要生产商品,那么肯定需要拿到商品的原材料以及制作配方,我们的Bean也是这样,Bean工厂需要拿到Bean的信息才可以去生成这个Bean的实例对象,而BeanDefinition就是用于存放Bean的信息的,所有的Bean信息正是从XML配置文件读取或是注解扫描后得到的。
我们接着来看,如果此BeanFactory不存在父BeanFactory或是包含了Bean的定义,那么会接着往下走,这时只能自己创建Bean了,首先会拿到一个RootBeanDefinition对象:
- try {
- if (requiredType != null) {
- beanCreation.tag("beanType", requiredType::toString);
- }
-
- RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
下面的内容就非常复杂了,但是我们可以知道,它一定是根据对应的类型(单例、原型)进行了对应的处理,最后自行创建一个新的对象返回。一个Bean的加载流程为:
首先拿到BeanDefinition定义,选择对应的构造方法,通过反射进行实例化,然后进行属性填充(依赖注入),完成之后再调用初始化方法(init-method),最后如果存在AOP,则会生成一个代理对象,最后返回的才是我们真正得到的Bean对象。
最后让我们回到ApplicationContext中,实际上,它就是一个强化版的BeanFactory,在最基本的Bean管理基础上,还添加了:
我们发现,无论是还是的构造方法中都会调用refresh()方法来刷新应用程序上下文:
- public AnnotationConfigApplicationContext(Class>... componentClasses) {
- this();
- this.register(componentClasses);
- this.refresh();
- }
此方法在讲解完AOP原理之后,再进行讲解。综上,有关IoC容器的大部分原理就讲解完毕了。