Spring注解方式加载Bean定义信息(新)
2021-03-16 16:05:07 1 举报
Spring注解方式加载Bean定义信息
作者其他创作
大纲/内容
调用重写方法
在配置类上,找到了含有元注解类型的注解
获取Resource资源:Resource resource = this.resourceLoader.getResource(resolvedLocation)
@Role
判断标准:①判断该类是否是独立的一个类(是否有父类,是否有静态内部类等)②判断类是不是基础类(即:既不是接口也不是抽象类)
返回为true时,直接通过candidates.add() 添加到 List中
this()方法
调用如下方法:postProcessor.postProcessBeanDefinitionRegistry(registry)
@Bean注解的beanDefinition注册到Spring容器流程
请按Ctrl + 滚轮放大查看
解析完成属性后,再次调用registerBeanDefinition()方法,将使用@Bean注解的类定义beanDefinition注册到Spring容器中
调用无参构造
⑥
返回false,说明没有被excludeFilters过滤掉,继续处理includeFilter的逻辑
调用processConfigBeanDefinitions()方法,基于注册类创建并验证(配置模型)
doScan()方法
处理includeFilters
①
是接口,直接返回false
registerBeanDefinition()方法
不是接口,执行第②步操作
②开始注册beanDefinition
1.后置处理器是否实现PriorityOrdered接口
返回false,继续for循环遍历
读取完成后,将数据add至Set集合,返回,开始类路径BeanDefinition扫描工作
scanner它自己重新定义了个ExcludeFilter(排除过滤器),重写了matchClassName()方法
org.springframework.context.annotation.internalPersistenceAnnotationProcessor
@EventListener
初次加载,默认是没有指定名称的
没有@Bean。继续下一次循环
②
做3步判断
调用 determineBeanNameFromAnnotation ()方法,获取beanName
③判断内部类是否被@Bean注释?
org.springframework.context.event.internalEventListenerFactory
分3种情况:
③
根据value值和默认的@ComponentScan中的resourcePattern默认属性值,组成要扫描的包的实际路径:比如:classpath*:com/example/web/**/*.class
没有,返回false
@Primary
return 直接返回
判断组件是否通过@Import方式导入
@ComponentScan注解的beanDefinition注册到Spring容器流程
① 解析候选配置类parser.parse(candidates)
配置注解处理器
matchClassName()方法
我们自己配置的
loadBeanDefinitionsForConfigurationClass()方法
执行3步操作
元注解说明:我们在SpringBoot中用到的@Configuration,它可以指定当前类为配置类。我们获取@Configuration的元注解类型(可以得到@Component 和 @Indexed两个),说明该注解被元注解类型修饰 (说明它就具有元注解特性)
ClassPathScanningCandidateComponentProvider
判断组件中是否有通过@Bean注解方式导入的Bean
3
调用registerAnnotationConfigProcessors()方法
parse()方法
invokeBeanFactoryPostProcessors()方法
Tip:我们自定义的bean实现BeanDefinitionRegistryPostProcessor接口,此处调用的postProcessBeanDefinitionRegistry()方法,就是调用我们重写的方法
不知道@PropertySource注解干嘛的?参考:@PropertySource 注解的使用
读取Spring内部管理的6大类默认后置处理器,添加到Set<BeanDefinitionHolder>类中
ClassPathBeanDefinitionScanner
②初始化类路径Bean定义(BeanDefinition)扫描器this.scanner = new ClassPathBeanDefinitionScanner(this);最终:创建一个类路径下的Bean定义扫描器类
@Lazy
该方法会为@Component 注册默认过滤器。这将会隐式注册具有@Component元注释的所有注释,包括@Repository @Service @Controller和@Controller型注释。@Controller、@Repository 也是一个 @Component,这就是Spring能够过滤到 @Component注解的原因try{} 语句块中,还会处理JSR-250 @ManagedBean 和 JSR-330 javax.inject.Named ,是否将其添加至默认过滤器,此部分在项目中基本不常用,不做介绍。此处就默认@Component为默认过滤器即可。
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
这两个类是同一个类哦
processConfigurationClass()方法
matchSelf()方法
注意:这一步骤会在所有candidates组件全部遍历完成后,才会执行registerBeanDefinition()方法,将Bean定义注册到Spring容器中
for循环遍历扫描到的路径下的所有文件
所有属性如下所示:
调用
根据路径,读取包路径下资源,以数组方式保存
processConfigBeanDefinitions()方法
2.后置处理器是否实现Ordered接口
②判断内部类是否被这4个注解注释?
初次启动Spring,这几个变量均为空
refresh()方法
②处理BeanDefinition(Bean定义)的注解参数配置。即:查看这个Bean类是否有被其他注解修饰(诸如:@Lazy @Primary 等)
在配置类上,没有找到元注解类型的注解
BeanDefinitionReaderUtils
目的:在使用@ComponentScan扫描时,如果主配置类也在扫描的这个包下,则会通过这个ExcludeFilter将自己排除掉,省去再次扫描主配置类工作(否则会再次去扫描主配置类,导致死循环)
AnnotationBeanNameGenerator
参考:@Import方式导入Bean组件的3种方式
判断@ComponentScan扫描到的配置文件,是否被@Component 或者@ManagedBean 修饰,如果被修饰,说明是一个组件,需要添加到Spring容器中,返回true,反之返回false(@ManagedBean不常用,可以只关注@Component即可)
返回结果
①初始化Bean定义(BeanDefinition)阅读器this.reader = new AnnotatedBeanDefinitionReader(this);
②Spring扩展点:BeanFactoryPostProcessor(这是个链接,支持点击跳转)
调用registerBeanDefinition()方法进行注册
有@Bean注解,开始解析
通过@Improt方式导入的组件,在此时beanName还是null,所以在此处会对这种方式的bean命名(命名规则:类的全限定类名)
调用findCandidateComponents()方法
比如:我们在SpringBoot中用到的@Configuration,它可以指定当前类为配置类。获取元注解类型(可以得到@Component 和 @Indexed两个),说明该注解被元注解类型修饰,我们可以通过value属性来获取指定的beanName(bean名称)
AnnotationConfigUtils
永远返回false
loadBeanDefinitionsForBeanMethod(beanMethod)方法
processMemberClasses()方法
此处参数为:可变长参数,可传入多个Class类,
processPropertySource()方法
我们自定义的bean实现BeanDefinitionRegistryPostProcessor接口,在执行重写的postProcessBeanDefinitionRegistry()方法时,会先调用getBean()获取Bean对象的所有内容(比如:重写的接口方法)
postProcessBeanDefinitionRegistry()方法
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
ConfigurationClassBeanDefinitionReader
创建Set结合,接收@ComponentScan扫描到的组件,然后添加到这个Set集合中
③开始注册BeanDefinition(bean定义)
registerBean(componentClass)方法
返回true,说明被excludeFilters过滤掉,循环包扫描中的下一个的Resource
@Autowired
调用addPropertySource()方法,找到指定占位符,使用propertySources.replace()方法,进行替换
开始解析注解:@ImportResource
此处是处理excludeFilters和b style=\
getBean()流程:
③如有别名,开始注册别名
没有被修饰,执行第③步操作
注册表处理器
如果没有指定value属性值
@DependsOn
就是通过这里的第⑥步来判断的
isCandidateComponent(MetadataReader metadataReader )方法
此过程其实就是将beanDefinition存放到Map中,key为beanName,value为beanDefinition
内部类解析完成,再次对candidates这个List 调用 processConfigurationClass()方法
loadBeanDefinitions()方法
1
存放至List中
AnnotatedBeanDefinitionReader
如有多个配置类(多个类使用@Configuration),并且有使用@Order属性标注顺序,则先根据@Order的value值进行排序
⑤
Spring一贯规则:do开头方法就是实际干活的,开始调用doRegisterBean()方法
(初始化 & 调用 BeanFactory的后置处理器)
判断自己的配置类(configCandidates)是否为空(即:是否有使用@Configuration注解)
此处定义了3个变量需要关注一下
doProcessConfigurationClass()方法
开始解析注解:@ComponentScan
否
调用registerDefaultFilters() 来注册一个默认的过滤器
开始对刚才的configCandidates,我们自己的配置类进行do{}while() 循环
开始解析注解:@PropertySource
①获取bean名称
④
ComponentScanAnnotationParser
Set<BeanDefinition> candidates = new LinkedHashSet<>();
Spring默认
font color=\"#ff0000\" style=\"\
generateBeanName()方法
是否是AnnotationBeanDefinition注解类型
二者区别,请参考:①Spring扩展点:BeanDefinitionRegistryPostProcessor(这是个链接,支持点击跳转)
获取所有的beanName名称,判断是我们自己配置的?还是Spring默认配置的?
org.springframework.context.annotation.internalCommonAnnotationProcessor
无法通过注解,来获取bean名称
Spring一贯规则do开头方法就是实际干活的,使用do{} while() 方式,开始调用doProcessConfigurationClass()方法,解析配置类
还有一些其他不重要的操作
有,返回true
先执行exclude调用tr.match(),返回false才会执行紫色路线的include
⑦
执行BeanDefinitionRegistryPostProcessors后置处理器
是
最终调用方法,将所有的beanDefinition注册到Spring容器中(即:beanDefinitionMap中)
步骤:①获取我们自定义配置类MainConfig的beanName(bean名称)②将配置类解析成BeanDefinition③注册BeanDefinition
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
isCandidateComponent(AnnotatedBeanDefinition beanDefinition)方法
②通过@Import + BeanDefinitionRegister 方式导入Bean组件:else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)){...}
开始解析每一个带有@Configuration注解的类
AnnotationConfigApplicationContext
@Description
获得value值:com.example.web
两个后置处理器说明:regularPostProcessors:存放BeanFactoryPostProcessor类型后置处理器registryProcessors:存放BeanDefinitionRegistryPostProcessor类型后置处理器
3种情况都会调用该方法解析Bean定义,xxxOrdered接口只是用来配置初始化顺序的
开始扫描,参数传入value值
调用Bean定义注册表后处理器方法
invokeBeanDefinitionRegistryPostProcessors()方法
getBean()流程,跳转链接参见:Spring 依赖注入(DI) 源码解析时序图
doRegisterBean()方法
match()方法
解析basePackages属性(basePackages 和 value 属性是别名关系)
2
往BeanDefinitionRegistry注册表中注册所有相关的注释后置处理器
jpa规范
3.registryProcessors
主要功能:会将配置@ComponentScan 和 @Bean 方式的组件,全部加入到Spring容器中来
ConfigurationClassPostProcessor
就是解决怕重复扫描主配置类,造成死循环
通过注解的value属性,来获取bean名称(比如:@Configuration(value=\"xxx\"))
通过@Import导入Bean组件,有3种方式,该方法中就是对这3种方式进行处理
register()方法
② 加载配置类中的bean定义(即:解析@Bean)reader.loadBeanDefinitions(configClasses)
include调用tr.match()
org.springframework.context.event.internalEventListenerProcessor
③通过@Import + ImportSeletor 方式导入Bean组件:else{...}
调用reader的register() 方法this.reader.register(componentClasses);注册一个或多个要处理的组件类。调用register是幂等的; 多次添加同一组件类不会产生任何其他影响。
调用 buildDefaultBeanName() 方法,生成唯一bean名称:类名首字母小写
register(componentClasses)方法
AbstractTypeHierarchyTraversingFilter
jsr250规范
处理excludeFilters
拼接扫描的包路径
解析扫描到的配置类中所有标注@Bean注解的方法。解析完成后,add到对应配置类的BeanMethod()属性种
①通过@Import方式导入Bean组件:if (candidate.isAssignable(ImportSelector.class)){...}
refresh()方法特别重要,用来加载或刷新Spring 配置,使配置生效
为空
获取配置类上的注解,开始for遍历这些注解,判断是不是指定的类型(通过获取当前注解的元注解类型的方式来判断)
直接调用scanCandidateComponents()方法,开始扫描value值所指定的包下组件
DefaultListableBeanFactory
@EventListenerFactory
省去重复扫描配置类,在@ComponentScan扫描的包路径下,如果主配置类也在扫描的这个包下,for循环遍历包路下的所有资源,如果遍历的文件正好是目前的主配置类,则通过matchClassName比对名称的方式,将自己直接排除掉
①@Import②@Component③@ImportResource④@ComponentScan
determineBeanNameFromAnnotation()方法
调用buildDefaultBeanName()生成一个 beanName
递归处理配置类中是否有嵌套内部类(如有,则对内部类进行处理)
不作任何处理
for( )循环调用registerBean()方法,将给定的类进行注册
有被修饰,直接返回true
挨个解析@Bean的各个属性,比如:autowire、initMethod、destroyMethod 等
processCommonDefinitionAnnotations(abd);
调用loadBeanDefinitionsForBeanMethod(beanMethod)方法
没有嵌套内部类时
正在处理中的Bean
这一步@ComponentScan注解扫描是重点,目的是将包下带元注解@Component的组件都添加到Set 集合中(包括内部类)但是在配置类中,通过@Bean方式引入的组件,并不会在这一步被加载
创建个List存放满足条件的内部类
返回true,将该组件添加到 Set 集合中,作为候选者组件
开始解析注解:@Import
调用processPropertySource()方法
①获取bean名称(beanName)
根据注解来决定bean名称(beanName)
此处内容,需要跳转链接查看
获取@PropertySource注解的value属性 String[] locations = propertySource.getStringArray(\"value\");
PostProcessorRegistrationDelegate
ConfigurationClassParser
2.regularPostProcessors
scanCandidateComponents()方法
存储注册信息的BeanDefinition,注意是BeanDefinition
①判断嵌套内部类是接口吗?
再来一次判断是不是基础组件
元注解类型,只有以下5种
processImports()方法
对这些注解进行解析,如有配置相对应注解,那么将其添加到BeanDefintion定义中(就是一个简单的set设置属性的过程)
for循环遍历扫描到的配置类组件,开始解析组件中通过@Bean导入的组件
3.后置处理器没实现任何接口
findCandidateComponents()方法
普通的后置处理器
1.processedBeans
如上图中的configCandidates,存放到List中,供接下来使用
0 条评论
下一页