品Spring:SpringBoot发起bean定义注册的“二次攻坚战”

上一篇文章整体非常轻松,因为在容器启动前,只注册了一个bean定义,就是SpringBoot的主类。

OK,今天接着从容器的启动入手,找出剩余所有的bean定义的注册过程。

具体细节肯定会颇为复杂,同样,大家只需关注都干了什么,不用考虑如何干的。

来宏观的看下容器的启动过程,即refresh方法,如下图01:

只捡重要的来说,就是四大步:

第一,准备好bean工厂(BeanFactory)。

第二,调用已经注册的bean工厂后处理器(BeanFactoryPostProcessor)。

第三,注册bean后处理器(BeanPostProcessor)。

第四,实例化所有的单例bean。

其中第二、第三引入两个新概念,“bean工厂后处理器”和“bean后处理器”。

为了更好的理解它们,再来赘述一遍和bean相关的操作过程。

注册bean定义 -> ① -> 实例化bean -> 依赖的装配 -> ② -> 初始化bean -> ③ -> OK

这个就是从一开始到bean实例准备好的整个流程。其中①②③是Spring预留的三个埋点,可以在这些地方插入一些用户代码,进行一些定制化。

其中①位于bean定义已经注册好后,尚未开始生成bean实例时,此处就是用来自定义处理bean定义的。

剩余②和③位于bean实例的初始化方法执行之前和之后,此处就是用来自定义处理bean实例的。

所以①处就对应于上述的第二,即bean工厂后处理器。②和③处就对应于上述的第三,即bean后处理器。

因此,可以看出,在容器启动过程中,能够和bean定义搭上关系的只有上述的第二,就是bean工厂后处理器。

它就是接下来我们的突破口。可是会有人问,从一开始到现在,明明没有人注册过它,为什么这里会调用它呢?

咦,这个问题问的好。不妨倒推一下,首先,肯定是注册了这个bean工厂后处理器了,不然这里为啥要调用,不然剩余的那些bean定义是如何注册到容器里的?

既然我们(即用户代码)没有注册,那一定是系统(即框架代码)自动注册了。好吧,只能姑且这样认为了。

那就找吧,肯定是隐藏在了某个地方。找啊找啊找朋友,找找找。

终于,功夫不负有心人,找到了,它隐藏在了两个类中,就是两个负责注册bean定义的类。

AnnotatedBeanDefinitionReader这个类的构造函数中,如下图02:

ClassPathBeanDefinitionScanner这个类的scan方法中,如下图03:

我们发现它们执行的是相同的代码,这不就执行两遍了吗?哈哈,里面做了幂等处理啦。

进到这个方法里会发现注册了好几个bean,但是bean工厂处理器的只有一个,如下图04:

它的bean名称里带了个internal,说明是内部使用的,即“基础设施”的作用,如下图05:

这个类的名称以ConfigurationClass开头,表示是对标有@Configuration注解的类的全权处理。

仔细想一下,让你在Spring家族中选择一个最特殊、最常见的注解,任何人都会选择@Configuration这个注解。

再仔细体会下,这个注解其实具有类似“配置”和“管理”方面的功能。可以说整个Spring都是围绕着它构建起来的。

OK,现在即将迎来本文的核心内容,打起精神来。照例还是侧重整体过程,弱化具体实现细节。

第一,取出已经注册的bean定义,其实就是主类自己这个光杆司令。

第二,判断它是否需要被处理,满足的条件是:

1)标有@Configuration注解

2)标有@Component注解

3)标有@ComponentScan注解

4)标有@Import注解

5)标有@ImportResource注解

6)含有@Bean方法

只要这六个条件满足其一,就需要被处理。

由于SpringBoot的主类上标有@SpringBootApplication注解,所以上述第1条就已满足了。

因此主类需要被处理,这不废话嘛,目前只注册了它自己,必须的被处理啊。

编程新说注:每一个符合条件的类,都可以认为是一个源(Source,即源泉),它的作用就是向容器中贡献bean定义。

第三,如果类上标有@Component注解,就去处理它的静态内部(嵌套)类,如下图06:

这其实已经是递归了,所以处理方式是一样的。

第四,接着处理@PropertySource注解,它可以引入.properties文件,会把文件中的属性值放入到Environment中。如下图07:

第五,接着处理@ComponentScan注解,它会扫描指定的jar包,并从中获取bean定义,如下图08:

第六,然后再处理@Import注解,如下图09:

该注解共可以引入三类内容:

1)另一个普通类,但是当作@Configuration类

2)ImportSelector接口的实现类

3)ImportBeanDefinitionRegistrar接口的实现类

其中第2、3是通过实现接口,自己写代码来注册bean定义,超级灵活,随意掌控。

编程新说注:这种方式的一般典型用法是,在实现第三方框架和Spring框架整合时使用。

请看下代码,如下图10:

第七,然后处理@ImportResource注解,它用于引入.xml文件,可以使xml和注解两种方式混合使用,如下图11:

第八,然后再处理类中的@Bean方法,如下图1213:

第九,然后再处理接口里面的默认方法,且方法上有@Bean注解的,如下图1415:

第十,最后再处理父类,如下图16:

因为每个@Configuration类除了自身是源之外,还可以向容器贡献其它的源,所以总体是递归进行的。

在进行的过程中,做好了防重复处理,所以不会出现重复注册。

以上所有这些其实都是ConfigurationClassPostProcessor这类里面的逻辑。

它不仅仅是一个bean工厂后处理器,还是一个专门用于注册bean定义的后处理器。

这个类在容器启动时会被调用,因此把其它类的bean定义注册到了容器中。

>>> 品Spring系列文章 <<<

品Spring:帝国的基石

品Spring:bean定义上梁山

品Spring:实现bean定义时采用的“先进生产力”

品Spring:注解终于“成功上位”

品Spring:能工巧匠们对注解的“加持”

品Spring:SpringBoot和Spring到底有没有本质的不同?

品Spring:负责bean定义注册的两个“排头兵”

品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”

作者是工作超过10年的码农,现在任架构师。喜欢研究技术,崇尚简单快乐。追求以通俗易懂的语言解说技术,希望所有的读者都能看懂并记住。下面是公众号和知识星球的二维码,欢迎关注!

       

原文地址:https://www.cnblogs.com/lixinjie/p/taste-spring-009.html

时间: 2024-11-09 12:27:14

品Spring:SpringBoot发起bean定义注册的“二次攻坚战”的相关文章

品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”

上一篇文章强调了bean定义注册占Spring应用的半壁江山.而且详细介绍了两个重量级的注册bean定义的类. 今天就以SpringBoot为例,来看看整个SpringBoot应用的bean定义是如何注册进容器的. 先来看看经典的启动入口,如下图01: 可以看到调用的是run方法,并把主类(main或primary)作为第一个参数出入. 接下来要做的事情,就是顺藤摸瓜,看看到底发生了什么,并确定下究竟哪些类被注册了bean定义. 此时,我就是一个快乐的小侦探,OK,走起. 上面的调用走到了这里,

spring直接获取bean定义

转:http://blog.csdn.net/sdandan/article/details/7911241 ———————————————————————————————————————— BeanFactory为一个管理bean的工厂(即为spring的容器),它管理的对象可以是bean也可以是FactoryBean(这种请况会再调用FactoryBean的getObject()获取真正的bean). FactoryBean为一个工厂bean,受BeanFactory管理. 先来看一看Bea

品Spring:注解之王@Configuration和它的一众“小弟们”

其实对Spring的了解达到一定程度后,你就会发现,无论是使用Spring框架开发的应用,还是Spring框架本身的开发都是围绕着注解构建起来的. 空口无凭,那就说个最普通的例子吧. 在Spring中要启用一项XXX功能,标准做法就是用@EnableXXX这种“启用”类型的注解. 那么这种类型的注解一般都做了什么呢?分析一下吧. 看过本号文章的人都知道,Spring的核心就是bean定义和bean,如果我想增加某方面的功能,只需写若干个类,并作为bean定义注册到容器中即可. 因此“启用”类型的

品Spring:对@Autowired和@Value注解的处理方法

在Spring中能够完成依赖注入的注解有JavaSE提供的@Resource注解,就是上一篇文章介绍的. 还有JavaEE提供的@javax.inject.Inject注解,这个用的很少,因为一般都不会去引用JavaEE的jar包. 编程新说注:JavaEE早已经被Oracle抛弃了.JavaEE这个名字已经成为历史. 还有两个就是@Value和@Autowired注解,这可是Spring自己的亲孩子.所以这两个使用的最多. 虽然注解不一样,但是目的一样,都是用来进行依赖注入,而且Spring处

品Spring:SpringBoot和Spring到底有没有本质的不同?

现在的Spring相关开发都是基于SpringBoot的. 最后在打包时可以把所有依赖的jar包都打进去,构成一个独立的可执行的jar包.如下图13: 使用java -jar命令就可以运行这个独立的jar包.如下图14: 这个jar包的执行入口就是一个main函数,典型的格式如下: @SpringBootApplicationpublic class TasteSpringApplication {    public static void main(String[] args) {     

品Spring:注解终于“成功上位”

历史还是抛弃了XML,当它逐渐尝到注解的甜头之后. 尤其是在Spring帝国,到处充满着注解的气息. 注解从一个提供附属信息的“门客”,蜕变为颇具中流砥柱的“君侯”. 注解成功登上了帝国的舞台,定会像XML一样留下浓墨重彩的一笔. 重新认识一下注解 注解其实就是注释.批注的意思.就像看书时在旁边记笔记一样. 如果把书上印刷的内容看作是原始信息,那写上的笔记则是新添加的额外信息,且这个额外信息并不会对原始信息造成破坏. 所以注解其实是为原始数据信息添加额外附加数据信息的一种方式,且对原有数据信息没

品Spring:bean定义上梁山

认真阅读,收获满满,向智慧又迈进一步... 技术不枯燥,先来点闲聊 先说点好事高兴一下.前段时间看新闻说,我国正式的空间站建设已在进行当中.下半年,长征五号B运载火箭将在海南文昌航天发射场择机将空间站核心舱发射升空.预计用2到3年将空间站建好. 虽然到时你们不让我上去,不过我也为这件事出不了什么力,算扯平了.哈哈,但是我还是会衷心的祝福你. 长征五号火箭首次采用5米大直径的箭体结构,总加注量达到780吨,起飞时共有10台发动机产生1078吨的推力,具备近地轨道25吨.地球同步转移轨道14吨的运载

021 使用@Import注解完成Bean的注册

一 . 概述 在之前我们使用@Bean或者组件扫描器加上组件标记注解向容器中注册组件. 在spring之中,也提供了另外一种方式帮助我们注册组件. 这个组件就是@Import的方式. 二 .注解的测试 组件类: public class Person { } 配置类: @Configuration @Import({Person.class}) public class ImportConfig { } 测试类: @RunWith(SpringJUnit4ClassRunner.class) @

Spring IOC基于源码的理解(二)

一.知识了解 1.Beanfactory和Application,BeanFactory和FactoryBean 内容在Spring基本知识点(一)后半部分可见: 2.BeanDefinition BeanDefinition是IOC容器体系非常重要的核心数据结构,Spring通过BeanDefinition来管理基于Spring的应用中的各种对象以及他们之间的相互依赖关系,实际就是POJO对象在IOC容器中的抽象.在DefaultListableBeanFactory中使用数据结构Concur