框架源码专题-Spring-8、BeanDefinition
1. BD 分类
2. BD 来源
2.1. 配置类及其注入的 bean
2.2. 指定的类路径
3. RootBeanDefinition 逻辑流程⭐️🔴
^f4b3wu
3.1. 注册 Root BD
入口 :new AnnotationConfigApplicationContext(MainConfig.class)
3.2. 创建 ConfigurationClassPostProcessor
刷新第5步:invokeBeanFactoryPostProcessors()
getBean ConfigurationClassPostProcessor
❕ ^na0djy
4. 用户 BD 解析流程 (Annotated)⭐️🔴
4.1. 流程图
Annotated:https://www.processon.com/diagraming/63dd8eb4b59543238fa3a6a5
XML:https://www.processon.com/diagraming/63ddb70fc12afe0cadb59d2c
4.2. 读取配置类 (注册配置类)
4.2.1. BeanDefinitionReader 接口
1. XmlBeanDefinitionReader 从 XML 配置中读取 BeanDefinition;
2. PropertiesBeanDefinitionReader 从 Properties 文件读取 BeanDefinition;
3. AnnotatedBeanDefinitionReader 对带有@Configuration 注解的BeanDefinition 进行注册;
4.2.2. AnnotatedBeanDefinitionReader⭐️🔴
^8skkg4
4.2.2.1. 注册位置
4.3. 扫描配置类 (解析配置类)
^zs22cl
作用:处理配置的 BeanDefinition 信息,把配置类中所有 bean 的定义信息导入进来。
4.3.1. 扫描入口
4.3.2. 解析流程概览
执行 ConfigurationClassPostProcessor
的 postProcessor.postProcessBeanDefinitionRegistry()
方法,会把【配置类中的】所有 bean 的定义信息导入进来。
期间由 ConfigurationClassParser 解析每一个配置类:
获取所有已经注册的 BeanDefinition 的 beanName,遍历中筛选对应的 beanDefinition(被注解修饰的),添加到 List<BeanDefinitionHolder> configCandidates
交给 ConfigurationClassBeanDefinitionReader
注册到 BeanDefinitionMap 中
判断当前 BeanDefinition 是否是一个配置类,并为 BeanDefinition 设置属性为 lite 或者 full:如果 Configuration 显示配置 proxyBeanMethods 代理为 true 则为 full,如果加了@Bean、@Component、@ComponentScan、@Import、@ImportResource 注解,则设置为 lite
4.3.3. ConfigurationClassParser⭐️🔴
获取并遍历所有已经注册的 BeanDefinition,通过 checkConfigurationClassCandidate
方法筛选出被注解修饰的并放入 configCandidates
集合中
用 dowhile 循环遍历上面一步得到的相关的 BeanDefinitionHolder 对象集合
Spring 的工具类 ConfigurationClassParser
用于分析@Configuration 注解的配置类,产生一组 ConfigurationClass 对象。它的分析过程会接受一组种子配置类 (调用者已知的配置类,通常很可能只有一个),从这些种子配置类开始分析所有关联的配置类,分析过程主要是递归分析配置类的注解@Import,配置类内部嵌套类,找出其中所有的配置类,然后返回这组配置类。❕
这个工具类自身的逻辑并不注册 bean 定义,它的主要任务是发现@Configuration 注解的所有配置类并将这些配置类交给调用者(调用者会通过其他方式注册其中的 bean 定义)
4.3.3.1. parse() : 外部调用入口
^lfmh74
1.将其封装成一个 ConfigurationClass
2.调用 processConfigurationClass(ConfigurationClass configClass)
分析过的每个配置类都被保存到属性
this.configurationClasses
中
4.3.3.2. doProcessConfigurationClass()
对一个配置类执行真正的处理
- 一个配置类的成员类 (配置类内嵌套定义的类) 也可能适配类,先遍历这些成员配置类,调用 processConfigurationClass 处理它们;
- 处理配置类上的注解@PropertySources,@PropertySource
- 处理配置类上的注解@ComponentScans,@ComponentScan
- 处理配置类上的注解@Import
- 处理配置类上的注解@ImportResource
- 处理配置类中每个带有@Bean 注解的方法
- 处理配置类所实现接口的缺省方法
- 检查父类是否需要处理,如果父类需要处理返回父类,否则返回 null
4.3.4. ComponentScanAnnotationParser⭐️🔴
对于非@Configuration 注解的其他 bean 定义,比如@Component、@ComponentScans、@ComponentScan 注解的 bean 定义,使用另外一个工具 ComponentScanAnnotationParser
扫描和注册它们。ComponentScanAnnotationParser
在扫描到 bean 定义时会直接将其注册到容器,而不是采用和 ConfigurationClassParser 类似的方式交由调用者处理。
4.3.4.1. 两者的关系⭐️🔴
- 在
ConfigurationClassPostProcessor
中使用ConfigurationClassParser
解析配置类过程中,使用到了ComponentScanAnnotationParser
ComponentScanAnnotationParser
处理@ComponentScans,@ComponentScan 注解。在扫描到 bean 定义时会直接将其注册到容器,而不是采用和 ConfigurationClassParser 类似的方式交由调用者处理。ComponentScanAnnotationParser
最终所使用的扫描器是ClassPathBeanDefinitionScanner
4.3.5. ClassPathBeanDefinitionScanner
4.3.5.1. 概括描述
- 在
ComponentScanAnnotationParser
中使用了ClassPathBeanDefinitionScanner
- ClassPathBeanDefinitionScanner 是一个从指定包内扫描所有组件 bean 定义的 Spring 工具。
工作时,它接收一组包的名称,然后在这些包内扫描所有的类,查找其中符合条件的 bean 组件定义并将这些 bean 组件定义注册到容器。这些 bean 定义注册到容器时具体使用的类为ScannedGenericBeanDefinition
,这是 Spring bean 定义模型接口 BeanDefinition 的一个具体实现类,针对扫描得到的 bean 定义。
4.3.5.2. 处理注解⭐️🔴
@Component
@Repository
@Service
@Controller
@RestController
@ManagedBean
(Java EE 6
)@Named
(JSR-330
)
https://blog.csdn.net/andy_zhang2007/article/details/85705001
4.3.6. ConfigurationClassBeanDefinitionReader⭐️🔴
this.reader.loadBeanDefinitions(configClasses)
,最后一步注册的核心方法,将完全填充好的 ConfigurationClass 实例转化为 BeanDefinition 注册入 IOC 容器
该内部工具由 Spring BeanDefinitionRegistryPostProcessor ConfigurationClassParser 使用。在容器启动过程中, ConfigurationClassParser 会在 BeanDefinitionRegistryPostProcessor 应用阶段被调用,用于发现应用中所有的配置类 ConfigurationClass,然后交给 ConfigurationClassBeanDefinitionReader 将这些配置类中的 bean 定义注册到容器。
5. postProcessBeanFactory⭐️🔴
5.1. 方法作用
❕
该方法是对 BeanFactory
进行处理,用来干预 BeanFactory
的创建过程。主要干了两件事: ^mcyor3
- 对被
@Configuration
标注的类,使用 CGLIB 代理生成代理对象。 - 向
beanPostProcessors
集合中添加一个ImportAwareBeanPostProcessor
Bean 的后置处理器
5.2. full 版和 lite 版⭐️🔴
❕ ^4zhdzi
https://www.processon.com/diagraming/63e37dc46330b9282f70ecc0
processConfigBeanDefinitions → ConfigurationClassUtils.checkConfigurationClassCandidate
❕
- 配置类组件之间无依赖关系用 Lite 模式,减少判断,加速容器启动过程
- 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用 Full 模式
❕
[[Spring的@Configuration配置类-Full和Lite模式_demon7552003的博客-CSDN博客]]
https://juejin.cn/post/7122064723037650980
对比记忆
exposeProxy:1、Spring-基础
❕
在线程中暴露代理对象 : 目标方法执行时实际上是调用的动态代理的方法,动态代理会拦截并调用 invoke(JDK) 或者 intercept(Cglib) 方法,在此期间会设置代理对象到 AopContext 中,已备获取使用。
JDK 动态代理:
❕
Cglib 动态代理:
❕ ^uqfm1u
6. processor 关系
BeanDefinitionRegistryPostProcessor 是 bean 定义信息注册器的后置器,它的执行时机为所有的 bean 定义信息(BeanDefinition)将要被加载到容器的时候,但此时 Bean 实例还没有被实例化;它与 BeanFactoryPostProcessor 区别在于,BeanFactoryPostProcessor 执行是在 所有的 bean 定义信息已经加载到容器的时候,因此 BeanDefinitionRegistryPostProcessor 的执行优先于 BeanFactoryPostProcessor 的执行,它们的相同点是 执行时 bean 实例都没被 实例化;
7. 扩展使用
如要对配置文件进行加解密,这个可以在 bean 定义信息已加载完成,但 bean 未实例化前进行处理,这个可使用 BeanFactoryPostProcessor
处理(如 jasypt-spring-boot-starter 就是类似这种处理的);
8. 实战经验
9. 参考与感谢
9.1. 拓薪教育
9.2. 网络笔记
https://www.cnblogs.com/coder-zyc/p/14583011.html
https://blog.csdn.net/andy_zhang2007/article/details/78549773
https://blog.csdn.net/andy_zhang2007/article/details/78579464