【Spring6源码・IOC】BeanDefinition的加载
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
哎呀又是午夜时分又是一个失眠的夜晚和去年一样记得去年今日也是睡不着觉就注册了csdn的账号开始写东西csdn真是深夜最好的安魂剂。
Spring都发布了6.0这不赶紧看看源码咱们来一起学习学习废话不多说了开始吧。
文章目录
IOC核心流程简介
环境Spring6、SpringBoot3.0、JDK17
读源码小tips
- 多看注释
- 梳理整体设计思想
- don’t care
IOC是一个容器对象的创建、使用和销毁都是由IOC容器来管理。
小白可以先看这篇《五分钟带你速通Spring IOC》
大体的流程如下
- 加载配置文件XML\YAML…、配置类并解析成BeanDefinition
- BeanFactoryPostProcessor对BeanDefinition做一些处理
- 实例化bean对象
- 初始化bean对象属性填充等并且在初始化前后通过BeanPostProcessor对bean对象进行相关处理
所以我们先从第一步开始BeanDefinition的封装
BeanDefinition的设计思想
BeanDefinition是存储Bean的元信息包括Bean本身的信息以及Bean注解信息。这个信息就是从我们的配置文件以及配置类等加载以及进行一系列处理而来。
以注解这种方式来说大概有三种
- 以@ComponentScan扫描的方式包括@Controller、@Service、@Repository、@Component。
- 以@Configuration声明的配置类内部会用@Bean来声明需要创建的对象
- @Import用来引入其他组件
首先会构建一个解析器去扫描所有的@Controller、@Service、@Repository、@Component、@Configuration类
通过这三种方法加载 BeanDefinition并将他们放到缓存beanDefinitionMap中。
那么是如何解析这些注解的时机是什么我们来一起看一看
解析@ComponentScan
以SpringBoot的启动来说我们通过SpringApplication#run最终调用AbstractApplicationContext 的 refresh() 方法。
invokeBeanFactoryPostProcessors这个方法就是用来加载BeanDefinition。
通过如下方法
- PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
- ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
- ConfigurationClassPostProcessor#processConfigBeanDefinitions
最后进入到ConfigurationClassPostProcessor 的 processConfigBeanDefinitions 方法
这里是找到我们启动类的BeanDefinition然后构建了一个ConfigurationClassParser解析器去扫描我们加上注解的类并加载为 BeanDefinition。
进入 parse 方法最终我们会来到老套路了方法前面加do的都是比较核心的方法包括后面扫描的时候有一个doScan。
进入到 ConfigurationClassParser 的 doProcessConfigurationClass 方法
这里可以看到这个又有一个解析器ComponentScanAnnotationParser它是用来处理@Controller、@Service、@Repository、@Component、@Configuration这些注解的。
我们来看看它做了什么事。
首先构建了一个ClassPathBeanDefinitionScanner对象然后对它进行一些set操作最后进入核心方法doScan中。
首先findCandidateComponents(basePackage) 方法会扫描启动类所在的包默认找到符合条件的类被@ComponentScan扫描到的以及@Configuration最后在通过registerBeanDefinition(definitionHolder, this.registry)方法将BeanDefinition注册金beanDefinitionMap中。
我们可以细讲一下这两个地方首先看如何筛选类的。
findCandidateComponents(basePackage) 方法最后进入下面这个核心方法
首先通过getResourcePatternResolver().getResources(packageSearchPath)加载出所有的类并封装成Resource数组然后在通过isCandidateComponent(metadataReader)筛选出符合的类最后构建成BeanDefinition类添加到set集合中最后返回。
registerBeanDefinition(definitionHolder, this.registry)方法呢就比较直接了如果在缓存中获取不到就直接加锁然后添进缓存中。
到此@ComponentScan 就扫描完成了BeanDefinition 也加载完成了。
解析@Bean
大多时候@Bean是和@Configuration一起使用的由上文可知@Configuration相关类在ComponentScanAnnotationParser处就会被解析到。
如图上面这个方法是上文一直在讲解的讲@ComponentScan扫描到的类加入缓存中下面这个就是真正解析@Bean和@Import并将其加入缓存的方法。
我们来看看吧因为一个配置类可能不止一个@Bean所以循环对每一个@Bean处理
最后通过this.registry.registerBeanDefinition(beanName, beanDefToRegister)将@Bean相关BeanDefinition添加进beanDefinitionMap中。
解析@Import
其实@Import更为简单一些这里优先判断其是否为@Import。
在parser.parse 方法中先将 类转化为一个 ConfigurationClass 类设置到它的 importedBy 属性中。然后在之后的 loadBeanDefinitions 方法中判断 ConfigurationClass 的 importedBy 属性是否为空如果不为空说明是需要加载的将它加载为 BeanDefinition最后registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass)方法中的this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition())将BeanDefinition添加进IOC容器中。
完事了。等下一章吧