Spring Boot - IOC(三)

本篇解析6-10步骤:

// Register bean processors that intercept bean creation.
//4.7.6 注册Bean的后置处理器
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
//4.7.7 初始化MessageSource(SpringMVC)
initMessageSource();

// Initialize event multicaster for this context.
//4.7.8 初始化事件派发器
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
//4.7.9、4.8 子类的多态onRefresh
onRefresh();

// Check for listener beans and register them.
//4.7.10 注册监听器
registerListeners();

6. registerBeanPostProcessors:注册 BeanPostProcessor

(源码较长,关键注释已标注在源码中)

发现这段套路与前面看到的注册 BeanFactoryPostProcessor 极其类似!

这里面有几个特殊的组件,着重看一眼:

6.1 MergedBeanDefinitionPostProcessor

它是一个接口,它的文档注释原文翻译:

Post-processor callback interface for merged bean definitions at runtime. BeanPostProcessor implementations may implement this sub-interface in order to post-process the merged bean definition (a processed copy of the original bean definition) that the Spring BeanFactory uses to create a bean instance. The postProcessMergedBeanDefinition method may for example introspect the bean definition in order to prepare some cached metadata before post-processing actual instances of a bean. It is also allowed to modify the bean definition but only for definition properties which are actually intended for concurrent modification. Essentially, this only applies to operations defined on the RootBeanDefinition itself but not to the properties of its base classes.

在运行时用于合并bean定义的后处理器回调接口。 BeanPostProcessor 实现可以实现此子接口,以便对Spring BeanFactory 用于创建bean实例的合并bean定义(原始bean定义的已处理副本)进行后处理。

postProcessMergedBeanDefinition 方法可以例如内省bean定义,以便在对bean的实际实例进行后处理之前准备一些缓存的元数据。还允许修改bean定义,但只允许修改实际上用于并行修改的定义属性。本质上,这仅适用于 RootBeanDefinition 本身定义的操作,不适用于其基类的属性。

文档注释似乎并没有说明太多意思,它是说给 BeanDefinition 做合并。借助IDEA,看一眼它的实现类:

img

这里面有一个我们一看就很兴奋: AutowiredAnnotationBeanPostProcessor

6.1.1 【重要】AutowiredAnnotationBeanPostProcessor

它的文档注释非常长,这里我们截取重要的部分:

BeanPostProcessor implementation that autowires annotated fields, setter methods and arbitrary config methods. Such members to be injected are detected through a Java 5 annotation: by default, Spring's @Autowired and @Value annotations. Also supports JSR-330's @Inject annotation, if available, as a direct alternative to Spring's own @Autowired. Only one constructor (at max) of any given bean class may declare this annotation with the 'required' parameter set to true, indicating the constructor to autowire when used as a Spring bean. If multiple non-required constructors declare the annotation, they will be considered as candidates for autowiring. The constructor with the greatest number of dependencies that can be satisfied by matching beans in the Spring container will be chosen. If none of the candidates can be satisfied, then a primary/default constructor (if present) will be used. If a class only declares a single constructor to begin with, it will always be used, even if not annotated. An annotated constructor does not have to be public. Fields are injected right after construction of a bean, before any config methods are invoked. Such a config field does not have to be public.

BeanPostProcessor 的实现,可自动连接带注解的字段,setter方法和任意config方法。通过Java 5注释检测要注入的此类成员:默认情况下,Spring的 @Autowired@Value 注解。 还支持JSR-330的 @Inject 注解(如果可用),以替代Spring自己的 @Autowired 。 任何给定bean类的构造器(最大)只能使用 "required" 参数设置为true来声明此批注,指示在用作Spring bean时要自动装配的构造器。如果多个不需要的构造函数声明了注释,则它们将被视为自动装配的候选对象。将选择通过匹配Spring容器中的bean可以满足的依赖关系数量最多的构造函数。如果没有一个候选者满意,则将使用主/默认构造函数(如果存在)。如果一个类仅声明一个单一的构造函数开始,即使没有注释,也将始终使用它。带注解的构造函数不必是public的。 在构造任何bean之后,调用任何配置方法之前,立即注入字段。这样的配置字段不必是public的。 Config方法可以具有任意名称和任意数量的参数。这些参数中的每个参数都将与Spring容器中的匹配bean自动连接。 Bean属性设置器方法实际上只是这种常规config方法的特例。 Config方法不必是public的。

很明确,它就是完成自动注入的Bean后置处理器。它实现了 MergedBeanDefinitionPostProcessor ,那自然要实现接口中的方法:postProcessMergedBeanDefinition

这里面分两步,先获取注入的依赖,再进行对象检查。分步骤来看:

6.1.1.1 findAutowiringMetadata

这部分实现中使用了双检锁来保证线程安全,之后会构建自动装配的 metadata:

6.1.1.2 buildAutowiringMetadata

先看一眼这个 do-while 循环,这个 do-while 循环是用来一步一步往父类上爬的(可以看到这个循环体的最后一行是获取父类,判断条件是判断是否爬到了 Object)。

循环体中,先是反射遍历当前类的属性,并判断上面是否有 @Autowired 等类型的注解。这部分注解的加载在这个方法中可以追溯到:

可以发现这部分判断的几种注解: @Autowired@Value@Inject

之后又获取方法上的注解,也保存进去。最后获取父类,一层一层往上爬,直到循环跳出,方法结束。


下面要到 checkConfigMembers 方法了:

6.1.1.3 checkConfigMembers

这里面涉及到一个叫 Member 的概念:

Member is an interface that reflects identifying information about a single member (a field or a method) or a constructor.

反映有关单个成员(字段或方法)或构造函数的标识信息的接口。

看文档注释的意思,大概可以看出来它是表示类中的一个成员。

源码中的for循环,里面有两个很迷的方法。这两个方法都操作了 RootBeanDefinition 的一个属性:externallyManagedConfigMember ,而这部分除了这两个方法有调过,也没别的地方用了。这两个方法除了这个方法中使用过,别的地方也没用过。那看来这部分不会影响到大局,大可忽略。

至此,咱先对 AutowiredAnnotationBeanPostProcessor 这个后置处理器作一个了解,自动注入的原理会在后续慢慢看到。


再来看一个后置处理器,它是在注册BeanPostProcessor中的最后一步,显式声明的。

6.2 ApplicationListenerDetector

注意上面的截图,会发现 ApplicationListenerDetector 也实现了 MergedBeanDefinitionPostProcessor 。而且这个类在之前第11篇的3.3章节介绍过它,它的作用是收集监听器。它是 BeanPostProcessor ,但同时它也是 MergedBeanDefinitionPostProcessor 。那咱来看看它实现 MergedBeanDefinitionPostProcessor 后实现的方法:

可以发现非常简单,只是保存Bean是否为单实例Bean的信息。这个单实例Bean的机制在前面也提到过,只有单实例Bean才能注册到监听器列表中。

至此,registerBeanPostProcessors 方法执行完毕。

7. initMessageSource:初始化MessageSource

这个组件我们在之前第7篇的IOC容器介绍(1.3.2章节)中说过,它是实现国际化的接口。

它默认创建的实现类是 DelegatingMessageSource ,它的文档注释:

Empty MessageSource that delegates all calls to the parent MessageSource. If no parent is available, it simply won't resolve any message.

Used as placeholder by AbstractApplicationContext, if the context doesn't define its own MessageSource. Not intended for direct use in applications.

空的MessageSource,将所有调用委派给父MessageSource。如果没有父母可用,它将根本无法解决任何消息。

如果上下文未定义其自己的MessageSource,则AbstractApplicationContext用作占位符。不适用于直接在应用程序中使用。

其实,DelegatingMessageSource 扮演的角色更像是一种 “消息源解析的委派”(用户未指定时,IOC容器会默认使用 DelegatingMessageSource )。它的功能比较简单:将字符串和参数数组格式化为一个国际化后的消息。

8. initApplicationEventMulticaster:初始化事件派发器

源码中先判断IOC容器中是否有名称为 applicationEventMulticaster 的Bean,没有就默认注册一个 ApplicationEventMulticaster

8.1 ApplicationEventMulticaster

它的文档注释原文翻译:

Interface to be implemented by objects that can manage a number of ApplicationListener objects, and publish events to them.

由可以管理多个 ApplicationListener 对象并向其发布事件的对象实现的接口。

可以发现它就是一个事件发布器而已。它的核心方法-事件发布的源码如下:

可以发现它最终会执行到 ApplicationListeneronApplicationEvent 方法,思路比较简单。

9. onRefresh:子类扩展刷新

发现又是模板方法。这部分我们单独留到第16篇再展开描述,SpringBoot 在这里做了额外的操作。

10. registerListeners:注册监听器

监听器在IOC容器中早就注册好了,取出来后要放入事件广播器,以方便事件广播器广播事件。

在上面方法的最后一段,它广播了早期事件。

之前在最开始我们遇见过早期事件(refresh的第一步),下面咱要真正的说说这个早期事件了。

10.1 earlyEvent:早期事件

refresh 方法的 prepareRefresh 中,最后一步有这么一句:

这里存储的事件会在这一步被触发。由此也知早期事件的发布时机:监听器被注册,但其余的单实例Bean还没有创建时

实际上,通过Debug,发现默认情况下这里根本就没有早期事件:

img

由此也大概猜到这个早期事件的设计由来:留给开发者,在后置处理器和监听器都被创建好,其余的单实例Bean还没有创建时,提供一个预留的时机来处理一些额外的事情

10.2 【扩展】SpringFramework中的观察者模式

实际上 ApplicationListenerApplicationEvent 这样的事件派发机制就是观察者模式的体现。

事件派发器(广播器)、事件监听器(被通知者)、事件(ApplicationEvent),其实这就是构成观察者模式的三大组件

  • 广播器(ApplicationEventMulticaster):观察事件发生

  • 被通知者(ApplicationListener):接收广播器发送的广播,并做出相应的行为

11. finishBeanFactoryInitialization:初始化单实例Bean

源码分为好几部分,前面的部分都不算很关键,注释已标注在源码中,最后一句代码是核心关键点:

11.1 preInstantiateSingletons

它跳转到了 DefaultListableBeanFactorypreInstantiateSingletons 方法:

上面的一系列判断后(判断逻辑已标注在源码上),如果不是工厂Bean,则会来到一个我们超级熟悉的方法: **getBean**

11.2 【核心】getBean

(源码超级长。。。)

这部分源码超级长!先大概浏览一下上面的源码和主干注释,下面咱们分段来看。

11.2.1 transformedBeanName:别名-BeanName的映射

transformedBeanName 方法往下调:

发现它就是拿aliasMap去一个个的取,找别名映射的BeanName,找不到就返回原始名。

11.2.2 getSingleton:尝试获取单实例Bean(解决循环依赖)

可以发现这段代码是在处理重复实例化的。IOC容器会对单实例Bean单独存储,这个地方就是从IOC容器中找是否已经被实例化。由于这部分源码复杂度过高,咱们在下一篇咱专门研究IOC容器如何解决循环依赖的。

11.2.3 创建前的检查

这里面有一个检查循环依赖的方法: isPrototypeCurrentlyInCreation

它这个方法是创建原型Bean时会校验的。如果当前线程中在创建一个 scope=prototype 的Bean,并且当前要创建的Bean跟这个线程中创建的Bean的name一致,则会认为出现了多实例Bean的循环依赖,会引发异常。

11.2.4 标记准备创建的Bean

这里的标记过程:

最后一句:this.alreadyCreated.add(beanName); ,已经足够理解了。IOC容器会把所有创建过的Bean的name都存起来。

11.2.5 合并BeanDefinition,处理显式依赖

这部分会解析 @DependsOn 注解标注声明的Bean,并预先的构建它,被依赖的Bean也是通过 getBean 方法来创建,思路一致,不再赘述。

11.2.6 准备创建Bean

在try块中,要真正的创建Bean了。注意 createBean 方法是通过 getSingleton 方法传入匿名内部类,调用的 createBean 方法。先来看 getSingleton 方法:

11.3 getSingleton

注意这里做了很重要的一步:如果当前准备创建的Bean还没有在IOC容器中,就标记一下它:

11.3.1 beforeSingletonCreation

注意if的判断结构中,有一个 this.singletonsCurrentlyInCreation.add(beanName) ,它的作用就是把当前准备创建的beanName放入 singletonsCurrentlyInCreation 中。它的作用是解决循环依赖,咱下一篇专门来解释循环依赖的处理。

11.4 createBean

注意跳转到的类:**AbstractAutowireCapableBeanFactory**

这段源码中重要的部分已经标注了注释,这里面两个重要的部分:AOP的入口真正创建Bean的入口

11.5 resolveBeforeInstantiation:AOP

**InstantiationAwareBeanPostProcessor**BeanPostProcessor 的子接口,它的文档注释:

Subinterface of BeanPostProcessor that adds a before-instantiation callback, and a callback after instantiation but before explicit properties are set or autowiring occurs. Typically used to suppress default instantiation for specific target beans, for example to create proxies with special TargetSources (pooling targets, lazily initializing targets, etc), or to implement additional injection strategies such as field injection. NOTE: This interface is a special purpose interface, mainly for internal use within the framework. It is recommended to implement the plain BeanPostProcessor interface as far as possible, or to derive from InstantiationAwareBeanPostProcessorAdapter in order to be shielded from extensions to this interface.

BeanPostProcessor 的子接口,它添加实例化之前的回调,以及在实例化之后但在设置显式属性或发生自动装配之前的回调。

通常用于抑制特定目标Bean的默认实例化,例如创建具有特殊 TargetSource 的代理(池目标,延迟初始化目标等),或实现其他注入策略,例如字段注入。

注意:此接口是专用接口,主要供框架内部使用。建议尽可能实现普通的 BeanPostProcessor 接口,或从 InstantiationAwareBeanPostProcessorAdapter 派生,以免对该接口进行扩展。

划重点:抑制特定目标Bean的默认实例化。也就是说这个接口对应的部分是真正的AOP创建代理对象的部分!

关于AOP的部分,后面有专门的篇章来分析AOP的原理,此处先跳过。

11.6 doCreateBean

源码中发现Bean的创建需要几个重要的步骤:

  1. createBeanInstance:创建Bean对象

  2. addSingletonFactory:Bean放入缓存(涉及到循环依赖,下一篇详细介绍)

  3. populateBean:属性复制和自动注入

  4. initializeBean:初始化后处理

11.6.1 【真正实例化】createBeanInstance

这里面的几个重要环节简单总结:

11.6.1.1 getInstanceSupplier:特殊的callback回调方法

这两个方法来自 AbstractBeanDefinition

它只是简单地get和set而已,那它这个 Supplier 又是从哪里来的呢?借助IDEA,发现在 GenericApplicationContext 中有一个调用:

这个方法我们还是比较熟悉的,它用来向 BeanFactory 中注册 Bean 的定义信息。这个方法又是从哪里调用的呢?继续借助IDEA查看:

img

发现在 GenericApplicationContext 中只有这一个地方有传入 supplier,其余的地方都是null。再索引这个方法的调用位置,发现跟上面图中的 DefaultControllerSpec 位置差不多了,都在这个类中。而这个类所在包是 org.springframework.test,是测试包中,我们不作关心。

那到这里来看,这个 Supplier 通常就是null了。

11.6.1.2 instantiateUsingFactoryMethod:工厂方法

这个工厂方法的由来,需要我们回到整个 refresh 方法的第5步:invokeBeanFactoryPostProcessors 。还记得当时在处理 BeanFactory 时回调了一组 BeanDefinitionRegistryPostProcessor 吗?它执行了一个很关键的后置处理器:ConfigurationClassPostProcessor 。不太记得的小伙伴请先翻回第12篇5.2章节回顾一下这部分,重要的环节是5.2.4章节。

在5.2.4章节中介绍了 @Bean 注解标注的方法的解析过程,这里面就有一个工厂方法的设置。那回到 doCreateBean 中,这里的 instantiateUsingFactoryMethod 方法就是对这种被 @Bean 注解标注的Bean进行创建。这个方法比较简单:

从方法名就可以很容易看出它是借助一个构造器处理器,来执行这个工厂方法中定义的Bean。这个方法的内容实在是太长了(220行+),我把关键的部分标注上注释,小伙伴们大概看一下整体思路就可以了:

源码好长,但总结下来就干了一件事:确定工厂方法 + 实例化、包装 BeanWrapper

源码中提到了一个比较有意思的概念:解析构造方法参数的严格模式/宽松模式

11.6.1.3 【扩展】严格模式/宽松模式

咱们单独把这一段摘出来:

默认情况下 lenientConstructorResolution 的值为true, 为严格模式。下面先看一眼这两种模式下的计算规则:

  • 严格模式下,必须要求参数类型完全一致

    • 这个方法的实现涉及到算法,不作细致研究,小伙伴们了解即可,感兴趣的小伙伴可以Debug运行看一下机制。

  • 宽松模式,只要参数是声明类型或子类型即可

    • 如果使用宽松模式,会出现一个问题:如果构造方法中传入两个接口,而这两个接口分别有两个实现类,此时IOC容器会觉得这两个对象都可以放到这两个参数中,造成权重一致,出现构造方法歧义

11.6.2 populateBean:属性赋值和自动注入

在源码中,通过那一组 InstantiationAwareBeanPostProcessor 就可以实现 @Autowired@Value 等自动注入。

如果是通过 setter 的方式进行自动注入,会走最后的一个if结构,调用 applyPropertyValues 方法。

下面分别分析这两种注入机制。

11.6.2.1 @Autowired 的自动注入

咱在之前13篇的6.1.1章节中介绍过一个后置处理器:**AutowiredAnnotationBeanPostProcessor** 。咱之前也说过,它就是实现 @Autowired 的自动注入。

它的类定义:

它集成了 InstantiationAwareBeanPostProcessorAdapter ,而 InstantiationAwareBeanPostProcessorAdapter 又实现了 SmartInstantiationAwareBeanPostProcessor 接口,SmartInstantiationAwareBeanPostProcessor 接口最终继承了 InstantiationAwareBeanPostProcessor 接口。那上面看到的核心回调方法就是 postProcessProperties

第一句咱们之前看过了,知道是构建自动注入的元数据,下面的 inject 方法是真正的自动注入。

11.6.2.2 [Autowired] inject

这里面最底下调了 element.inject 方法。借助IDEA,在打开这个方法时发现这个方法有两个子类重写了这个方法,分别是 AutowiredFieldElementAutowiredMethodElement 。很明显它们是给属性注入和方法注入的。我们以属性注入为例分析(关键源码的注释已标注在源码中):

上面的检查完成后,在try块中的核心方法可以用来关联创建被依赖的Bean:beanFactory.resolveDependency

11.6.2.3 [Autowired] beanFactory.resolveDependency

上面的一些if-else结构判断都是检验被标注 @Autowired 注解的属性类型,显然上面的一些类型一般都不用,直接来看下面的最后一个else结构:它调用 doResolveDependency 方法来解决依赖:

11.6.2.4 [Autowired] doResolveDependency

(核心步骤的注释已标注在源码中)

注意看源码中靠下部分的 descriptor.resolveCandidate(autowiredBeanName, type, this); ,在一开始什么Bean都匹配不到的情况下,Debug发现会来到这里,而这个方法的实现:

它回到getBean中,开始关联创建。

创建好后,回到 inject 方法:

利用反射注入属性值。至此,可以发现 @Autowired 的自动注入和关联创建流程。

11.6.2.5 [setter] applyPropertyValues

使用setter方法,前面的一大段都不走了,直接来到最后的 applyPropertyValues 方法。

进入到 applyPropertyValues 方法中(不太关键的部分注释已标注在源码中):

这里面的核心方法:**valueResolver.resolveValueIfNecessary**

11.6.2.6 [setter] resolveValueIfNecessary

解析上面几个标号的分支,了解属性赋值和自动注入的核心

11.6.2.7 [setter] RuntimeBeanReference

这部分跳转到了 resolveReference 方法:

发现这里面的核心还是getBean方法!开始触发关联创建Bean。

11.6.2.8 [setter] 解析List

跳转到 resolveManagedList 方法:

这里面直接把 List 集合塞入属性中即可。

至此,属性赋值和自动注入得以体现,至于这里面如何解决循环依赖,下一篇详细描述。

11.6.3 initializeBean:初始化Bean

11.6.3.1 invokeAwareMethods:执行注入的功能

可以发现这里面是对 BeanName 的注入,BeanClassLoader 的注入,以及 BeanFactory 的注入,实现很简单,不再展开。

11.6.3.2 applyBeanPostProcessorsBeforeInitialization:执行后置处理器

发现这里是真正的执行 BeanPostProcessor 的方法了,调用逻辑也很简单,不再展开。

11.6.3.3 invokeInitMethods:执行初始化Bean的操作

可以发现这里只有执行了 InitializiingBean 接口的 afterPropertiesSet 方法,没有找到 @PostConstruct 标注的方法。根据之前的分析,知道 @PostConstruct 标注的方法会先执行。那上面的源码中,执行 InitializingBean 的方法之前只有执行 BeanPostProcessor 了,那可以大概猜测是一个 BeanPostProcessor 执行了 @PostConstruct 方法。

通过给一个自定义的组件声明测试方法,并标注 PostConstruct ,发现它的调用栈里有一个 InitDestroyAnnotationBeanPostProcessor ,它的执行方法 postProcessBeforeInitilization 方法如下:

可以发现调用了 metadata.invokeInitMethods 方法执行了 @PostConstruct 标注的方法。

至此,可以发现初始化Bean的逻辑也调用完成,整个 doCreateBean 方法执行完毕。


回到 getBean 方法中:

createBean 执行完后,回到了匿名内部类的 getSingleton 方法:

11.7 匿名内部类执行完成后的getSingleton调用

调用完成后,finally 块中还有一步操作:

在if条件中,它要把当前Bean的name从 singletonsCurrentlyInCreation (正在被创建的Bean)中清除。

最终,将这个Bean添加到 singletonObjects (一级缓存)中,createBean 方法彻底完成。

11.8 最后的工作

回到 preInstantiateSingletons 中。

它又回调了一组类型为 SmartInitializingSingleton 的组件,来回调它们的 afterSingletonsInstantiated 方法。

11.8.1 SmartInitializingSingleton

它的文档注释原文翻译:

Callback interface triggered at the end of the singleton pre-instantiation phase during BeanFactory bootstrap. This interface can be implemented by singleton beans in order to perform some initialization after the regular singleton instantiation algorithm, avoiding side effects with accidental early initialization (e.g. from ListableBeanFactory.getBeansOfType calls). In that sense, it is an alternative to InitializingBean which gets triggered right at the end of a bean's local construction phase. This callback variant is somewhat similar to org.springframework.context.event.ContextRefreshedEvent but doesn't require an implementation of org.springframework.context.ApplicationListener, with no need to filter context references across a context hierarchy etc. It also implies a more minimal dependency on just the beans package and is being honored by standalone ListableBeanFactory implementations, not just in an org.springframework.context.ApplicationContext environment. NOTE: If you intend to start/manage asynchronous tasks, preferably implement org.springframework.context.Lifecycle instead which offers a richer model for runtime management and allows for phased startup/shutdown.

BeanFactory 引导期间的单实例bean的初始化阶段结束时触发的回调接口。该接口可以由单例bean实现,以便在常规的单例实例化算法之后执行一些初始化,避免意外的早期初始化带来的副作用(例如,来自 ListableBeanFactory.getBeansOfType 调用)。从这个意义上讲,它是 InitializingBean 的替代方法,后者在bean的本地构造阶段结束时立即触发。 这个回调变体有点类似于 org.springframework.context.event.ContextRefreshedEvent,但是不需要 org.springframework.context.ApplicationListener 的实现,不需要在整个上下文层次结构中过滤上下文引用。这也意味着更多对bean包的依赖性最小,并且由独立的ListableBeanFactory实现兑现,而不仅仅是在 ApplicationContext 环境中。 注意:如果要启动/管理异步任务,则最好实现 org.springframework.context.Lifecycle,它为运行时管理提供了更丰富的模型,并允许分阶段启动/关闭。

从文档注释来看,它是 InitializingBean 的替代方案,但通过上面的代码也了解,它的执行时机是:所有单实例Bean都创建完毕。小伙伴们大概对这个地方有个印象即可,后续的源码分析中可能会遇到,我会再往回提的。

最后更新于

这有帮助吗?