构造方法里会
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在创建的过程中会执行的