spring @bean 自动创建容器对象的原理
一、在applicationContext.refresh方法中会调用invokeBeanFactoryPostProcessors(beanFactory),此方法会调用当前系统容器中所有注册的BeanDefinitionRegistryPostProcessor的对象,最终会调用ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry。
二、在ConfigurationClassPostProcessor.processConfigBeanDefinitions会循环遍历当前容器工厂内的类元注解中包含configuration,componen,ComponentScan
、 Import
和 ImportResource的对象。
三、接着会调用
ConfigurationClassParser.parse会对每个上面筛选出来的对象进行解析,如果有condition注解在类上,则会判断条件注解是否满足,不满足直接过滤,满足条件的保存到
ConfigurationClass列表中。
1.@import注解的类解析在ConfigurationClassParser.doProcessConfigurationClass。
1.1.如果注解中的类是普通类,则会递归调用ConfigurationClassParser.processConfigurationClass进行解析,并保存到parser的configurationClasses中。
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->// process it as an @Configuration classthis.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
1.2 如果注解中的类是实现了ImportSelector接口,则会调导入类的selectImports接口获取具体要导入的类字符串数组,然后通过asSourceClasses转换成类,再次递归调用processImports,然后就循环到1.1步骤。
注意:这里有个如果类继承了DeferredImportSelector,则为延迟加载,会放在所有普通导入BEAN的最后进行加载,并可以控制容器内所有DeferredImportSelector的加载顺序(@ORDER),这个应用场景主要用于自动配置,@springbootApplication的注解引入的@EnableAutoConfiguration注解,里面引入的那个importSelect就是延迟selector,spring先处理我们的,然后再处理自动装配的,那些自动配置类上的那些条件就可以根据我们的配置情况来判断是否应该生效。
// Candidate class is an ImportSelector -> delegate to it to determine importsClass<?> candidateClass = candidate.loadClass();ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,this.environment, this.resourceLoader, this.registry);Predicate<String> selectorFilter = selector.getExclusionFilter();if (selectorFilter != null) {exclusionFilter = exclusionFilter.or(selectorFilter);}if (selector instanceof DeferredImportSelector) {this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);}else {String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);}
1.3.如果注解中的导入类是ImportBeanDefinitionRegistrar类,则会将此类保存到configurationClass的importBeanDefinitionRegistrars属性列表中。
Class<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
2.@bean注解的类解析在ConfigurationClassParser.doProcessConfigurationClass。这个相对简单,就是解析当前解析类中所有包含@bean注解的方法,并保存到configurationClass的beanMethods集合列表中。
// Process individual @Bean methodsSet<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}
动态数据如下:
3.@ComponentScan注解的类解析在ConfigurationClassParser.doProcessConfigurationClass,这个就是根据@COmponentScan配置的包路径列表逐个使用ComponentScanAnnotationParser.parse方法扫描包路径下的包,然后将获取的BeanDefinitionHolder集合列表再次递归调用parse(bdCand.getBeanClassName(), holder.getBeanName())进行处理,添加到parser的configurationClasses中。
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediatelySet<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}
4.@ImportResource注解的类解析在ConfigurationClassParser.doProcessConfigurationClass,这个注解用于导入Spring的配置文件,让配置文件里面的内容生效;(就是以前写的springmvc.xml、applicationContext.xml),这个解析也比较简单,只是将注解locations属性和BeanDefinitionReader的读取类作为MAP的键值保存到configurationClasse的importedResources列表属性中。
AnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}
四、然后会调用ConfigurationClassBeanDefinitionReader.loadBeanDefinitions 循环解析每个组件对象,将新增的@bean,@import符合条件的注解对象,注入到beanfactory工厂对象中。
if (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
1.@bean注解的BeanDefinition对象生成实现为ConfigurationClassBeanDefinition,并设置此BEAN的工厂方法为当前@BEAN注解的方法,工厂BEAN名称为当前对象,最后会调用容器工厂将此BeanDefinition注册进去保存到beanDefinitionMap,如下图
下面是配置类的代码:
package com.tpw.newday.service.people;import com.tpw.newday.bean.PeopleBean;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;/*** <h3>newday</h3>* <p></p>** @author : lipengyao* @date : 2021-07-01 15:38:01**/
@Configuration
@Conditional(BeanFactoryCondition.class)
public class MsgConf {@Bean(name = {"msgSpringBean"})@Conditional(SpringCondition.class)public PeopleBean msgSpringBean(){System.out.println(" MsgConf -->msgSpringBean: " );return new PeopleBean("msgSpringBean");}@Bean(name = {"msgOriginFactoryBean"})@Conditional(BeanFactoryCondition.class)public PeopleBean msgOriginFactoryBean(){System.out.println(" MsgConf -->msgOriginFactoryBean: " );return new PeopleBean("msgOriginFactoryBean");}
}
2.@importResource注解的BeanDefinition对象会先根据RESOURCE的配置文件路径生成XmlBeanDefinitionReader(register容器工厂作为参数),然后调用
reader.loadBeanDefinitions(resource),将配置中的所有BEAN实例注册到容器工厂中。
private void loadBeanDefinitionsFromImportedResources(Map<String, Class<? extends BeanDefinitionReader>> importedResources) {Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();importedResources.forEach((resource, readerClass) -> {// Default reader selection necessary?if (BeanDefinitionReader.class == readerClass) {if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {// When clearly asking for Groovy, that's what they'll get...readerClass = GroovyBeanDefinitionReader.class;}else {// Primarily ".xml" files but for any other extension as wellreaderClass = XmlBeanDefinitionReader.class;}}BeanDefinitionReader reader = readerInstanceCache.get(readerClass);if (reader == null) {try {// Instantiate the specified BeanDefinitionReaderreader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);// Delegate the current ResourceLoader to it if possibleif (reader instanceof AbstractBeanDefinitionReader) {AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);abdr.setResourceLoader(this.resourceLoader);abdr.setEnvironment(this.environment);}readerInstanceCache.put(readerClass, reader);}catch (Throwable ex) {throw new IllegalStateException("Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");}}// TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocationsreader.loadBeanDefinitions(resource);});}
3.@import中实现了ImportBeanDefinitionRegistrar接口,则会把configurationClass的importBeanDefinitionRegistrars属性列表所有导入BEAN注册表循环注册复制到容器工厂注册表中。这个比较简单。
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {registrars.forEach((registrar, metadata) ->registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));}
五、循环遍历当前新解析出来的对象,又回到第二步的逻辑,判断新对象中是否包含configuration,componen,ComponentScan
、 Import
和 ImportResource的注解,如果有,则添加到新的candidates中,再次循环,解析,加载数据。
// Parse each @Configuration classConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());do {parser.parse(candidates);parser.validate();Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}this.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);candidates.clear();if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}candidateNames = newCandidateNames;}}while (!candidates.isEmpty());
六、前面所有的工作都是注册
BeanDefinition到容器工厂,具体的BEAN真正实例化创建都是在最后的applicationContext.
finishBeanFactoryInitialization中去初始化非延迟的单例BEAN,这个前面文章已经讲过,不再复核。
spring @bean 自动创建容器对象的原理相关推荐
- 一文搞定 Spring Bean 的创建全过程!
作者:x1aoda1 来源:www.cnblogs.com/darope/p/13917264.html 1.1 Spring测试环境搭建 Spring模块概览,绿色是模块,Spring中八大模块,黑 ...
- spring Bean自动装配
spring Bean自动装配 自动装配是使用spring满足bean依赖的一种方式. spring会在应用上下文中为某个bean寻找其依赖的bean. spring自动装配需要从两个角度来实现,或者 ...
- spring bean的创建,生命周期
1. 初探spring 什么是spring Spirng 是分层的Java se/ee应用full-stack(web层mvc dao层jdbc模板 业务事务管理)轻量级开源框架,以IoC(inver ...
- Spring AOP自动创建代理 和 ProxyFactoryBean创建代理
Advice 通知类型 AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Advice,Spring按照通知Advice在目标方法的连接点位置,可以分为5种 ...
- Spring | Bean自动装配详解
个人主页:BoBooY的CSDN博客_Java领域博主 前言:上节我给大家讲解了Spring的依赖注入,这一节我们讲解Spring中Bean如何自动装配,废话不多说,直接上正文! 文章目录 Bean的 ...
- 调用百度地图出现的自动创建完成对象会清空输入框值的问题
最近开发一个调用百度的页面用来定位搜索的,然后各种网上找, map = new BMap.Map("container");//创建一个百度地图容器 map.centerAndZo ...
- Spring bean注入之注解注入-- @Autowired原理
之前我们已经讲述过bean注入是什么了,也使用了xml的配置文件进行bean注入,这也是Spring的最原始的注入方式(xml注入). 本节课就讲注解注入. 主要讲解的注解有以下几个: @Autowi ...
- Spring-AOP 自动创建代理
导读 概述 实现类介绍 BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator AnnotationAwareAspectJAutoProxyC ...
- 学习第三篇:【SpringBoot-Labs】芋道 Spring Boot 自动配置原理
本周(8.21-8.27)将学习芋道 Spring Boot的以下文章: 8.21: 快速入门 8.22:Spring Boot 自动配置原理 .Jar 启动原理 8.23:调试环境. 热部署入门.消 ...
最新文章
- docker_File 执行报错总结
- Linux内核中的进程等待与其实现解析
- git 提交代码命令_Git命令可视化展示,代码管理再也不愁了,建议收藏!
- WinRAR 注册方法
- 售达方、送达方的区别
- 再见 Win10系统!下一代操作系统要来了!!
- 微信公众号回调java_处理微信公众号消息回调
- 【转载】 详细介绍Flex中操作XML
- 前端学习(1317):静态资源2
- php漫画连载系统,小涴熊漫画连载系统带采集API开源
- 大学生适合学习的软件 网站推荐
- 计算机组成原理无符号数除法,计算机组成原理课设 不恢复余数的无符号阵列除法器.doc...
- 热血格斗场和冷血格斗场
- 桂林电子科技大学校园网登录数据包抓取教程实现路由器每天自动登录
- 手机和电视不在一个网络如何投屏?跨网段投屏方法
- Access-Control-Allow-Origin: Dealing with CORS Errors in Angular
- 要不要启用苹果wapi_苹果“史上最强”系统ios13来了,要不要升级?
- VS2019安装与使用教程
- 副族元素从上到下原子半径_原子结构与原子半径.PPT
- 439计算机毕业设计
热门文章
- 销售Invoice管理流程
- 爱奇艺六季度付费用户数据一览,巨头A股还有希望吗?
- 一个肯德基拖着6个“拖油瓶”的百胜中国,如何赢下中国市场?
- list所有元素相加 python_Python基础入门笔记:列表、元组(阿里云天池)
- cancase lin管脚_汽车里有什么信号是传送到总线(CAN/LIN)上?
- html 字号 宽度 像素,JS根据设备宽度设置根节点(html)font-size字体大小
- ajax 下拉框 保留,Ajax生成select级联下拉框和清空多余选项
- 无盘服务器 双镜像盘,镜像(无盘柜)-双活集群解决方案
- Echarts开源可视化库学习(一) 介绍与快速上手
- 【Python教程】删除字符串中字符的四种方法