Spring Bean生命周期源码解析(上)

构造方法里会

new一个扫描器(扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在@Component注解,那么就会把这个类解析为一个BeanDefinition)

BeanDefinition读取器(可以直接把某个类转换为BeanDefinition,并且会解析该类上的注解)

StartupStep

Spring5.3新增的接口

就两个实现类

什么都没做

Jdk9或11以后得新特性

JFR 就是 Java 的黑匣子

主要用于记录这行代码的运行时间是多少

第三行记录结束

refresh()方法里有个完成Bean工厂初始化的方法

这个方法内部会实例化非懒加载单例Bean

在refresh()方法里有个完成Bean工厂初始化的方法上面有个方法

在这里就会调用前面new的sacner.scan()方法去完成扫描

sacner.scan()

传进来一个包路径

这里有个接口registry,他有个重要实现类DefaultListableBeanFactory

在左上角

为什么这里不用DefaultListableBeanFactory呢?

因为没有必要,我扫描器主要的功能是把扫描得到的BeanDefinition注册到Spring容器中。

实际上这里DeafaultListableBeanFactory其实内部还是调用的BeanDefinitionRegistry接口的功能。

这些DeafaultListableBeanFactory还有很多功能用不到,scanner只需要去注册BeanDefinition,

但实际在运行的时候值还是DeafaultListableBeanFactory

doScan()

一般大家都进的else

addCandidateComponentsFromIndex()

可能项目中有很多很多类,就可以利用索引提高性能

直接告诉spring中项目中有哪些bean

包路径同样传进来==》包含过滤器

找出Component注解,然后调用addAll,传Component注解和包路径进去

把文件写到的内容都拿出来遍历

spring就不会去扫描了,直接从文件里找当前容器内有哪些文件的右边和Component注解匹配, 然后这些类是不是和我的包路径匹配,如果匹配则把这个类当做一个bean,收集起来

Set<String>types是收集的类的名字

然后遍历,拿到类的读取器(重复后面步骤)

scanCandidateComponents()

根据包路径会拼接一些"classpath*:" + 包路径 + "/**/*.class",然后根据ResourcePatternResolver工具类去获得资源。

实际上这里找出来的是一个个的class文件的file对象

案例1

当前有两个Bean

得到class对应的Resource

接下来会遍历每个Class

当前处理的是AppConfig.class,传进去就能得到元数据读取器,就可以读取类的信息啊,注解啊,是不是抽象类啊,父类啊之类的。底层用的ASM技术

接下来会通过if来判断这个类是不是一个Bean。

如果当前传的类和排除过滤器匹配了就返回false,就被排除的,不是一个Bean

然后再去检查当前这个类是不是要包含进来。

也就是说一定要匹配,否则默认是返回false的

Spring构造器中会默认注册一个@Component注解的

默认会有2个,一个Component,一个ManagedBean注解

所以类上只有这两个任意一个就会匹配。

接下来要进行条件匹配 ,光有Component注解还不够,拿到类上面的注解信息,

会去判断类上有没有Conditional注解,就会返回false,也就是不要跳过,那就是一个Bean

这里就会返回true

就能通过这个验证,返回true

@Conditional

实现Condition接口,如果说Classpath里有User这个类,不存在就返回false,存在返回true

把刚才定义的条件类写在注解里就能用了。

也就是说现在得符合这个条件UserService才能是一个Bean,否则就不是

当前UserService是有这个注解的(接下来就会去调用这个match方法,看是不是返回true,符合Bean的要求)

如果都符合是个Bean的条件,会去构造一个BeanDefinition

这是个构造方法,会把注解的元数据读取器的信息设置进metadata,

还会把当前类的名字设置给beanClass属性和Resource资源文件

这个类型为什么是Object,而不是Class????

因为当前只是扫描,并还没有解析,只有当创建这个bean的时候才会去加载这个类。

所以一开始赋值给beanClass的是类的名字,只有当要加载这个类的时候,才会去得到这个名字对应的class对象,然后把class这个对象赋值给这个beanClass 属性。所以一开始可能只是名字,后面就是Class对象了。

接下来还有一个判断

isCandiateComponent() 候选

取出类的元数据 不过这里是注解相关的元数据

还回去判断是不是独立的(不是独立的类就不能成为一个bean)

isConcrete是不是一个接口或者抽象类(接口和抽象类都没办法得到一个对象)

特殊情况:是抽象类 但是有@Lookup注解

内部类

内部类也会单独是一个class文件,spring也会扫描到,但这个内部类得先实例外部的类

才会再实例内部的类 。(静态内部类是独立的)

普通情况:他是抽象类,不能成为一个bean

这时候如果加了@Lookup注解

那么这个抽象类就是一个bean了

案例2

讲道理这个test方法,每次都要进行变化

但是都是一样的,原因是UserService是单例,它只会创建一次,

所以只会给User属性赋值一次,main方法里用的是同一个UserService对象,所以就不会变。

这里每次调用test()它都会去调用a()方法

@Lookup底层怎么调用后续说。

都符合最终加到candidates,返回

遍历所有BeanDefinition

接下来就会遍历BeanDefinition,解析出Scope的元数据,是单例还是原型然后设置仅scope。

首先解析当前@Component注解有没有指定名字,如果指定了就直接返回了

没指定就会构造一个默认的,首字母变小写

(特殊情况:第一个和第二个都是大写,那beanName用的还是这个)如:ABtest,beanName就是ABtest

接下来给BeanDefinition赋一些默认值(默认不是懒加载)

接下来在解析@Lazy注解、@Primary注解等设置给BeanDefinition属性

checkCandidate()

检查Spring容器中是否已经存在该beanName,传入beanName和当前对应BeanDefinition。

如果检查成功,则会把当前beanName和BeanDefinition封装成一个Holder对象

这部分先跳过

然后加入到beanDefinitions,注册

把BeanDefinition拿出来最后注册到bean工厂里面去

那前面到底检查什么呢?

如果容器中当前的beanName还没有则返回true

当前所生成的BeanDefinition的这个beanName传进来去容器中检查当前这个beanName是否已经有一个BeanDefinition了呢?

这一块先忽略掉

要么抛异常(当前这个beanName对应的类型已经存在)

要么返回false(返回false也不会注册到容器中)

定义2个@Component,名字都用"UserService"

兼容返回false的情况:

会去判断Resource是否相同,因为2个BeanDefinition肯定不同,但对应的是同一个类就认为是兼容的。

这里会扫描2次,但扫描都是同样东西,不应该出现问题的

一开始scan方法():

当前容器有多少个bean,扫描完

再用当前容器有多少个bean再减去之前的bean,就知道扫描出了多少个bean

扫描完之后

生成非懒加载的单例bean

前面方法都可以理解为扫描

这里会取出所有的beanName

遍历所有的beanName

getMergedLocalBeanDefinition(beanName)

根据beanName获取合并后的BeanDefinition,把内部的属性和parent的进行合并,如果自己定义了就用自己的,自己没定义用的就是父亲的。RootBeanDefinition

合并之后的BeanDefinition。合并会生成第三个BeanDefinition对象的,不能直接修改,因为父亲不只给一个人用,其他人还要用。不一定是要抽象的BeanDefinition才能成为parent,普通的BeanDefinition也能成为父亲

所以合并好的RootBeanDefinition就会存到mergedBeanDefinitions。

会检查当前传来的beanName有没有合并过,如果有就直接返回

如果没有就直接走这个方法

先根据beanName从BeanDefinitionMap中拿到BeanDefinition返回(此时是没有合并的)

getMeregedBeanDefinition

然后会调用getMeregedBeanDefinition方法,第三个参数是null

传进来的参数:UserService,UserService的BeanDefinition。

当前的bean没有parent

判断当前的bean有没有指定parent,首先会判断自己是不是一个RootBeanDefinition,如果是的话克隆一个(生成一个新的BeanDefinition,属性也会赋值过去给新的),如果本身不是RootBeanDefinition类型那我也会生成一个新的(把当前的BeanDefinition传进去进行属性的copy)

然后下面就会存到这个mergeBeanDefinitions,不会影响到原有的BeanDefinition

如果当前的bean有parent

走的是else的逻辑

transformedbeanName

首先拿到UserService你parent的名字,按照我们的例子parent是User,而beanName是UserService,

所以会进入if语句 进行递归(因为此时的User可能也是有父亲的,要合并父亲肯定也得要先合并)

父亲合并完之后才会轮到自己合并,这里pbd就是父BeanDefinition

先根据父BeanDefinition去生成RootBeanDefinition

因为新的mbd是基于父BeanDefinition去生成的

所以这边会用子的BeanDefinition去覆盖,子没有定义就还是延用父亲的

合并完得到新的BeanDefinition才会存到mergedBeanDefinitions里,真正创建bean其实用的是合并之后的

mergedBeanDefinitions

接下来判断BeanDefinition是不是单例,是不是非懒加载,是不是非抽象的BeanDefinition才会进if

抽象的BeanDefinition:

UserService的父亲是User

UserService继承了User的多例

isFactoryBean(beanName)

判断完是非抽象bean,非懒加载的单例bean。

就会判断是不是FactoryBean,大部分情况不是,走的else

怎么根据名字来判断是不是FactoryBean呢?

transformedBeanName

首先getSingleton,能不能从单例池拿到值呢?其实不能。因为还在最开始的位置

如果拿到了那拿到的就是FactoryBean实现的那个类,返回也肯定是true。

只有getBean()才会拿到那个真的bean

拿不到的话,只能通过基于BeanDefinition去判断是不是FactoryBean,如果本容器没有BeanDefinition,就会看有没有父BeanFactory再调用父亲的isFactoryBean

例子(父FactoryBean)

Application就是父子Bean工厂

此时可能我的context里没有这个bean,我就会去parent的容器(beanFactory)里去找

只会对应一个BeanDefinition,虽然会有2个Bean对象。

beanClass全类名

根据BeanDefinition去拿beanClass的属性,我就知道对应的类,然后去判断类有没有实现FactoryBean

接下来在beanName前加上"&"符号,首先会创建一个xxxFactoryBean对象,是FactoryBean接口的实现类对象(不是getObject()方法返回的那个对象)

判断当前是没有xxxFactoryBean对象有没有实现SmartFactoryBean接口(继承了FactoryBean的)的

如果实现了SmartFactoryBean接口且重写了isEagerInit()返回了true,就会在spring启动的时候生成User对象

没有实现就不会走下面的if,去生成getObject()返回的对象

如果传的是带"&"名字,希望得到的是xxxFactoryBean对象,如果不带“&”则希望是getObject()方法返回的对象

xxxFactoryBean是存在单例池里的,但是单例池里的xxxFactoryBean它的key是不带&的,所以这个方法会处理掉“&”这个符号,一堆“&”底层也用的while处理掉,然后再通过getSingleton(beanName)方法拿到xxxFactoryBean对象,如果不带“&”就直接返回,

这里会传入两个名字,"name:&xxx"和“beanName:xxx”

如果传的name带了"&"符号,从单例池拿出来的不是xxxFactoryBean是有问题的,如果是就直接返回。

比如你以为xxxFactoryBean是FactoryBean,所以加了&符号,但取出来不是,就会抛异常

假如我传的是“xxx”,说明我想拿到的是getObject()方法返回的对象,此时就不会进入这个if,而是往下走

先从缓存拿,一开始缓存是空,走下面的逻辑,直接调用getObject方法得到对象,放入缓存

getbean()

创建Bean对象

就会调用getBean方法

getBean方法里面会调用createBean

所有单例Bean创建完之后

遍历每个bean的名字,找出所有的单例bean对象

接下来判断单例bean对象是否实现了SmartInitializingSingleton接口,如果实现了则直接调用

afterSingletonsInastantiated()方法

容器所有的非懒加载的单例bean创建完之后一个个才会调用afterSingletonsInstantiated()方法,

而之前的那些是每个单例bean在创建的过程中会执行的

小总结:

扫描

扫描(addCandidateComponentsFromIndex)/(findCandidateComponents)==》拿到所有指定扫描路径下的Resource资源数组(里面都是一个个class文件,Spring源码中将此文件包装成了Resource对象)===》传入Resource获取类的元数据信息===》是否匹配排除过滤器===》是否包含include过滤器(ManagedBean/Components)===》条件匹配(是否有@Conditional注解,调用match方法)==》符合构造sgbd(ScannerGenericBeanDefinition)把注解的元数据赋值metadata,还会设置Resource资源文件,把class这个对象赋值给这个beanClass属性(以上内容都会在构造方法内完成)===》(isCondidateComponent方法)取出sbd中刚设置的类的元数据(注解相关的元数据)还会判断是不是独立的(不是独立的类就不能成为一个bean 静态内部类除外)以及isConcrete是不是一个接口或者抽象类(接口和抽象类都没办法得到一个对象)特殊情况:是抽象类 但是有@Lookup注解===》最终都符合最终加到Set<BeanDefinition> candidates,返回(scanCandidateComponents方法结束)

315

遍历每个Set<BeanDefinition> candidates,解析出scopeMetadata数据,设置进BeanDefinition的scope属性===》生成beanName(有指定直接返回,没有的话首字母小写或前2个字母大写返回)===》给当前beanDefinition赋值一些默认值(销毁方法,懒加载,强制初始化方法)===》解析@Lazy、@Primary、@DepondsOn等注解,设置给BeanDefinition===》检查当前spring容器中是否已经存在该beanName,如果有相同的beanName则会报错抛异常(但是如果Resource资源是一样的则返回false,说明是兼容的),spring容器中不存在该beanName则返回true===》check成功把BeanName和BeanDefinition封装成一个BeanDefinitionHolder对象,加到beanDefinitions,最后拿出BeanDefinition注册到beanDefinitionMap(用到最开始的registry)。

扫描完后

(生成非懒加载的单例bean)首先取出然后遍历所有的beanName<ArrayList>======》根据beanName获取合并后的BeanDefinition,把内部的属性和parent的进行合并,如果自己定义了就用自己的,自己没定义用的就是父亲的。合并会生成第三个BeanDefinition对象的,不能直接修改,因为父亲不只给一个人用,其他人还要用。不一定是要抽象的BeanDefinition才能成为parent,普通的BeanDefinition也能成为父亲,所以合并好的RootBeanDefinition就会存到mergedBeanDefinitions。首先会检查当前传来的beanName有没有合并过(存在于mergedBeanDefinitions),如果有就直接返回。没有就调用getMeregedBeanDefinition方法(传入beanName和未合并的BeanDefinition)===》判断当前的bean如果没有指定parent,首先会判断自己是不是一个RootBeanDefinition,如果是的话克隆一个(生成一个新的BeanDefinition,属性也会赋值过去给新的),如果本身不是RootBeanDefinition类型那我也会生成一个新的(把当前的BeanDefinition传进去进行属性的copy),然后下面就会存到这个mergeBeanDefinitions,不会影响到原有的BeanDefinition。===》调用transformedbeanName方法,首先拿到UserService你parent的名字,按照我们的例子parent是User,而beanName是UserService,所以会进入if语句 进行递归(因为此时的User可能也是有父亲的,要合并父亲肯定也得要先合并),先根据父BeanDefinition去生成RootBeanDefinition,然后子的BeanDefinition去覆盖属性,子没有定义就还是延用父亲的,合并完得到新的BeanDefinition才会存到mergedBeanDefinitions里,真正创建bean其实用的是合并之后的mergedBeanDefinitions。==》接下来判断BeanDefinition是不是单例,是不是非懒加载,是不是非抽象的BeanDefinition才会进if====》根据beanName判断,如果从单例池里拿到了那拿到的就是FactoryBean实现的那个类,返回也肯定是true。如果拿不到beanName对应的BeanDefinition就会从父beanFactory里调用isFactoryBean去找,如果没有父FactoryBean,就走到isFactoryBean方法里来(传入的参数:beanName和合并之后的BeanDefinition),会根据BeanDefinition的beanClass全类名来推断出Bean类型,判断出是否实现了FactoryBean的接口,然后会更新缓存属性isFactoryBean为true。接下来在beanName前加上"&"符号,创建一个xxxFactoryBean对象,是FactoryBean接口的实现类对象(不是getObject()方法返回的那个对象),接着会判断这个对象有没有实现SmartFactoryBean接口,如果实现了看有没有重写isEagerInit()返回了true,如果是true则会在Spring初始化的时候就创建getObject()返回的对象==》如果此时beanName传的是带"&"名字,希望得到的是xxxFactoryBean对象,如果不带“&”则希望是getObject()方法返回的对象,因为xxxFactoryBean是存在单例池里的,但是单例池里的xxxFactoryBean它的key是不带&的,所以这个transformBeanName方法会处理掉“&”这个符号,一堆“&”底层也用的while处理掉,然后再通过getSingleton(beanName)方法拿到xxxFactoryBean对象,如果不带“&”就直接返回,然后调用getObjectForBeanInstance(),这里参数会传入两个名字,"name:&xxx"和“beanName:xxx”,如果传的name带了"&"符号,从单例池中取出直接返回。但从单例池拿出来的不是xxxFactoryBean是有问题的,就会抛异常。

===》如果不是FactoryBean,此时就调用getbean()去创建非懒加载的单例bean了根据beanName来判断是不是FactoryBean==》

===》所有的非懒加载单例bean创建完之后,又会遍历每个bean的名字也就是beanName,getSingleton()找出所有的单例bean对象接下来判断单例bean对象是否实现了SmartInitializingSingleton接口,如果实现了则直接调用afterSingletonsInastantiated()方法【区别:容器所有的非懒加载的单例bean创建完之后,才会一个个调用每个单例bean的afterSingletonsInstantiated()方法,而之前的那些是每个单例bean在创建的过程中会执行的】

扫描(315)===》传入要扫描的包路径

两条路:

1.走的spring.components配置 走的索引,指定类路径和注解,效率可以快一点,不用逐级扫描

2.拼上前缀classpath*:+包路径+**/*.class,Resource工具类,获取苏有的Resource文件,封装成数组,遍历。

首先获取类的元数据信息。看是否命中排除过滤器,接下来看是否命中include过滤器,有两个注解:Component和ManagedBean?看是否命中条件匹配过滤器,类上是否有@Conditional注解指定了哪个类实现了Condition接口,调用match方法,返回true是一个Bean。接下来会构造扫描一般的BeanDefinition(sgbd),构造方法完成3件事,①取出注解的元数据信息赋值给metaData,②把全类名赋值给beanClass属性,这个是Object类型的,后面完成实例化才会把class对象封装进来,③封装Resource资源。接下来执行isCondidateComponents方法,是否是独立的(静态内部类除外),接口,抽象类(加了@LookUp注解除外)。执行isCondidateComponent(),符合就全部加入到Set<BeanDefinition> condidates里。

遍历condidates解析Scope的元数据信息,赋值给当前BeanDefinition。赋值BeanName(如果有指定),完成默认值的赋值,强制初始化,懒加载,销毁方法等,看是否有@Lazy,@DependsOn,@Primary注解等,赋值给BeanDefinition。Check检查当前BeanName对应的BeanDefinition是否存在于容器中,如果有抛异常,但是如果Resource一致就认为是兼容的。check成功根据BeanName和BeanDefinition封装成Holder对象,然后通过Register加入到BeanDefinitionMap里。

扫描后(121312)===》

遍历ArrayList<beanName>,首先会判断当前beanName对应的BeanDefinition是否合并过,合并过直接返回,未合并===》判断是否有parent,如果有,这个是个递归,因为要合并其父亲必须先合并,合并完最后放的mergeBeanDefinition,如果没有parent则新建个RootBeanDefinition,属性拷贝过去,放到mergeBeanDefinition。===》然后判断是否是非抽象,非懒加载,单例的(只有是才会去创建bean),如果是则判断是否是FactoryBean==》

如果是==》根据beanName去判断是否是FactoryBean的,首先会从单例池中通过beanName拿到(存放的就是FactoryBean的实现类),如果拿不到的话,就会(基于BeanDefinition做一个类型推断,如果当前beanName拿不到BeanDefinition,则会去父BeanFactory里去找),①通过beanClass拿到全类名,然后看是否实现了FactoryBean接口。beanName前加上&符号,创建xxxFactoryBean对象,②判断是否实现了SmartFactoryBean接口,重写了isEagerInit方法返回的是true,如果是name在spring容器启动的时候就会创建这个getObject()返回的对象。===》如果返回了true,这时候就会去创建真正的bean对象,如果此时getBean(createBean)的beanName传的是带"&"名字,希望得到的是xxxFactoryBean对象,如果不带“&”则希望是getObject()方法返回的对象,因为xxxFactoryBean是存在单例池里的,但是单例池里的xxxFactoryBean它的key是不带&的,所以这个transformBeanName方法会处理掉“&”这个符号,一堆“&”底层也用的while处理掉,然后再通过getSingleton(beanName)方法拿到xxxFactoryBean对象,如果不带“&”此时就直接返回,然后调用getObjectForBeanInstance(),这里参数会传入两个名字,"name:&xxx"和“beanName:xxx”,如果传的name带了"&"符号,就直接从单例池中取出返回。如果不带&说明我想拿的就是getObject()方法返回的对象,先从缓存拿,一开始缓存是空,走下面的逻辑,直接调用getObject方法得到对象。

如果不是==》

就会调用getBean方法,创建bean对象。

==》

所有单例Bean创建完之后

遍历每个bean的名字,找出所有的单例bean对象

接下来判断单例bean对象是否实现了SmartInitializingSingleton接口,如果实现了则直接调用afterSingletonsInastantiated()方法。

容器所有的非懒加载的单例bean创建完之后一个个才会调用afterSingletonsInstantiated()方法,

而之前的那些是每个单例bean在创建的过程中会执行的

  • 25
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值