SpringBoot的自动装配

  • 大致流程
  • 完整流程(结合源码)
    • 启动类
    • SpringApplication的构造方法
    • run方法
    • prepareContext()方法
    • load()方法
    • refreshContext()方法
    • refresh()方法
    • invokeBeanFactoryPostProcessors()方法
    • ConfigurationClassPostProcessor类中的processConfigBeanDefinitions()方法
    • ConfigurationClassParser类中的parse()方法
    • ConfigurationClassParser类中的doProcessConfigurationClass()方法
    • AutoConfigurationImportSelector类中的process()方法
    • 至此,springboot的自动装配就完成了

大致流程

  1. 当启动SpringBoot应用程序的时候,会先创建SpringApplication对象,在构造方法中会进行一些参数的初始化工作,比如会加载Spring.factories文件,将文件的内容放到缓存对象中,方便后续获取

  2. SpringApplication对象创建完成后,开始调用run方法,启动过程中最主要有两个方法,第一个叫prepareContext(),第二个叫refreshContext()方法

  3. 在prepareContext()方法主要是对上下文对象ConfigurableApplicationContext的初始化操作,在整个过程中有个非常重要的方法就是load()方法,它会将当前启动类作为一个BeanDefinition注册到BeanDefinitionMap中,方便后续在进行BeanFactoryPostProcessor调用执行的时候,找到对应的启动类,来完成对应注解的解析工作

  4. 在refreshContext()方法会进行整个Spring容器的刷新refresh操作,会调用spring的refresh()方法,自动装配过程是在invokeBeanFactoryPostProcessor方法()中进行(也就是执行BeanFactory的后置处理器),在此方法主要是针对ConfigurationClassPostProcessor类的处理

  5. 在执行BeanFactory后置处理器的时候会调用ConfigurationClassPostProcessor类中的parse()方法去解析处理各种注解比如@CompomentScan、@Import等等

  6. 在解析@Import注解的时候比较特别,会有一个collectImports()方法,从主类开始递归解析注解,把所有包含@Import的注解都解析到BeanDefinitionMap中

  7. 调用AutoConfigurationImportSelector类(相当于一个处理器)中的process()方法进而触发getCandidateConfigurations()方法获取Spring.factories文件下的key为EnableAutoConfiguration的所有value,所以这就是为什么很多人的文章中都说Springboot的自动装配就是调用@EnableAutoConfiguration注解下的@Import中的AutoConfigurationImportSelector类,主要就是通过这种不断解析注解的方法去调用的

  1. 将所有解析到的注解的类都注册到BeanDifinitionMap中

  2. 至此就完成了SpringBoot的自动装配

完整流程(结合源码)

启动类
@SpringBootApplication
public class Test {public static void main(String[] args) {SpringApplication.run(Test.class, args);System.out.println("启动成功");}
}
SpringApplication的构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {// 初始化this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();// 加载Spring.factories文件,将文件的内容放到缓存对象中,方便后续获取setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 设置监听器setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 将mainApplicationClass属性设置为当前类Test的Clas对象this.mainApplicationClass = deduceMainApplicationClass();
}
run方法
public ConfigurableApplicationContext run(String... args) {// 省略代码try {// 准备环境,这里的environment环境包括系统环境,比如jvm的参数等等ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 准备Banner,即每次启动项目的时候控制台都会出现一个很大的图像Banner printedBanner = printBanner(environment);// 创建上下文对象context = createApplicationContext();// 准备上下文对象prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 刷新上下文对象refreshContext(context);// 省略代码}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}
}
prepareContext()方法

准备上下文对象,将springApplicationArguments对象和springBootBanner对象放入Spring一级缓存singletonObjects中

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 省略代码// 获取Beanfactory对象ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();// 将springApplicationArgusments放到一级缓存中beanFactory.registerSingleton("springApplicationArguments", applicationArguments);// 将springBootBanner放到一级缓存中if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}// 加载资源,也就是将当前的启动类Test Class对象放到Set中Set<Object> sources = getAllSources();// 见下load(context, sources.toArray(new Object[0]));// 将上下文加载到监听器中listeners.contextLoaded(context);
}
load()方法

将当前启动类作为一个BeanDefinition加载到BeanDefinitionMap中,方便后续在进行BeanFactoryPostProcessor调用执行的时候,找到对应的启动类,来完成对应注解的解析工作

private int load(Class<?> source) {// 判断启动类上有没有@Component注解if (isComponent(source)) {// 若有则将当前启动类作为一个BeanDefinition注册到BeanDifinitionMap中this.annotatedReader.register(source);return 1;}return 0;
}
refreshContext()方法

执行完prepareContext()方法后,调用Spring的刷新容器方法refresh()

private void refreshContext(ConfigurableApplicationContext context) {// 调用Spring的刷新容器方法refresh(context);
}
refresh()方法

这个方法相信大家非常熟悉了吧,那完成Springboot自动装配过程是在哪个阶段发生的呢?其实就是在invokeBeanFactoryPostProcessors()方法实现的

// 伪代码,方法在AbstractApplicationContext类中
public void refresh() throws BeansException, IllegalStateException {// 执行BeanFactory的后置处理器,自动装配中此方法主要是针对ConfigurationClassPostProcessor类的处理invokeBeanFactoryPostProcessors(beanFactory);
}
invokeBeanFactoryPostProcessors()方法

这里主要就是处理BeanFactory后置处理器,也就是直接或者间接实现BeanFactoryPostProcessor的接口的类,主要调用的是ConfigurationClassPostProcessor中的postProcessBeanDefinitionRegistry()方法

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {BeanDefinitionRegistryPostProcessor registryProcessor =(BeanDefinitionRegistryPostProcessor) postProcessor;// 主要就是这个方法registryProcessor.postProcessBeanDefinitionRegistry(registry);}}}
}
ConfigurationClassPostProcessor类中的processConfigBeanDefinitions()方法

此方法作用就是去解析处理各种注解比如@CompomentScan、@Import等等注解

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {// ...List<BeanDefinitionHolder> configCandidates = new ArrayList<>();// 获取对应的BeanDifinitionMap中的BeanDefinitionNameString[] candidateNames = registry.getBeanDefinitionNames();// 创建注解解析器ConfigurationClassParser 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);} while (!candidates.isEmpty());// ...
}
ConfigurationClassParser类中的parse()方法

解析注解的入口

public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();if (bd instanceof AnnotatedBeanDefinition) {// 具体的解析注解的方法parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());}}this.deferredImportSelectorHandler.process();
}
ConfigurationClassParser类中的doProcessConfigurationClass()方法

到达解析注解的最终方法,将解析到的Bean注册到BeanDifinitionMap中

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {// 解析@PropertySource注解for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}// 解析@ComponentScan注解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());}}}}// 解析@Import注解processImports(configClass, sourceClass, getImports(sourceClass), true);// 解析@ImportResource注解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);}}// 解析@Bean注解Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}return null;
}
AutoConfigurationImportSelector类中的process()方法

这里的作用是读取Spring.factories文件中的内容,将所有的第三方的starter装载到BeanDefinitionMap中

public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {// getAutoConfigurationEntry()方法读取Spring.factories文件中的内容入口AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);this.autoConfigurationEntries.add(autoConfigurationEntry);for (String importClassName : autoConfigurationEntry.getConfigurations()) {this.entries.putIfAbsent(importClassName, annotationMetadata);}
}
至此,springboot的自动装配就完成了

创作不易,希望大家能够点个赞,也希望大家能帮忙指出问题,一起进步!!!谢谢大家~~

SpringBoot的自动装配相关推荐

  1. 【SpringBoot】自动装配原理

    [SpringBoot]自动装配原理 文章目录 [SpringBoot]自动装配原理 一.pom.xml 1.spring-boot-dependencies 2.spring-boot-starte ...

  2. springboot 无法自动装配

    @Autowired 报错:无法自动装配. 基本上是因为: 1.项目里有类似mybatis @Mapper这种第三方映射类,需要用到springboot autoconfigration扫描解析. 2 ...

  3. SpringBoot之自动装配原理

    1. 自动装配的使用 Spring Boot的核心理念是简化Spring应用的搭建和开发过程,提出了约定大于配置和自动装配的思想.开发Spring项目通常要配置xml文件,当项目变得复杂的时候,xml ...

  4. SpringBoot -> 自动装配初探,debug=ture判断配置类是否生效

    文章目录 第一个注解:@SpringBootApplication 点进去有一堆注解: 1.这4个是元注解: 2.@SpringBootConfiguration @EnableAutoConfigu ...

  5. springboot 自动装配_Spring Boot 自动装配流程

    Spring Boot 自动装配流程 本文以 mybatis-spring-boot-starter 为例简单分析 Spring Boot 的自动装配流程. Spring Boot 发现自动配置类 这 ...

  6. 这篇带你深入理解SpringBoot中的自动装配(好文精读)

    作者: 聂晨 cnblogs.com/niechen/p/9027804.html SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提.其实它并不那么神秘,我在这之前已经写过最基本的实 ...

  7. 启动rrt什么意思_面试官:你来说一下springboot启动时的一个自动装配过程吧!...

    前言 继续总结吧,没有面试就继续夯实自己的基础,前阵子的在面试过程中遇到的各种问题陆陆续续都会总结出来分享给大家,这次要说的也是面试中被问到的一个高频的问题,我当时其实没答好,因为很早之前是看过spr ...

  8. Springboot自动装配源码分析

    Springboot自动装配源码分析 1.从@SpringbootApplication点进去 2.可以看到核心的三个注解 @SpringbootConfiguration @EnableAutoCo ...

  9. SpringBoot 自动装配原理解析

    自动装配是 Spring Boot 的核心部分,也是 Spring Boot 功能的基础,正是由于自动装配,才 将我们从 Bean 的繁复配置中解脱出来.那么 Spring Boot 中的自动装配指的 ...

最新文章

  1. 视觉语言研究进展到哪了?CVPR2021视觉语言研究全面概述,附430页ppt
  2. 为什么智能车竞赛没有清华学生参加比赛呢?
  3. Leet Code OJ 242. Valid Anagram [Difficulty: Easy]
  4. vue2.0中的:is和is的区别
  5. 课堂笔记——Ubiquitous Computing
  6. 当电信诈骗犯遇上程序员,技术决定成败!
  7. 一个轻量级javascript框架的设计模式
  8. 实现文件中名词的统计计数_Python中的统计数据展示
  9. oracle学习日志---返回RemoteOperationException: ERROR: Wrong password for user-错误的用户名密码-的错误的解决办法...
  10. 更改android的avd的存储路径
  11. 痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(8)- 从Raw NAND启动
  12. 风花雪月·《基层风云·1》·四
  13. luogu P1653 猴子
  14. h5通过当前时间获取农历日期
  15. iOS:仿微信朋友圈的实现
  16. 腾讯云离线语音识别sdk
  17. 大掌门2显示服务器繁忙,大掌门2新手攻略_游戏功能详细攻略(新手攻略二)_软吧...
  18. 【数据库系统】——经典示例University数据库建表代码和添加数据(大学系统)
  19. Shared UI Mesh内存占用过高
  20. 已解决org.springframework.beans.factory.UnsatisfiedDependencyException org.springframework.beans.factor

热门文章

  1. 钮文新:余额宝摧毁实业精神
  2. 我的大学,追忆高三(永不能忘的332)
  3. sockaddr结构体
  4. ros uvc摄像头驱动安装
  5. 一公顷等于多少亩「知识普及」
  6. 说梦话都是代码级的,你信吗?
  7. 针式打印机打印自动跳页
  8. 萌新初学python
  9. 巴斯卡三角形(杨辉三角)
  10. c语言输出四行金字图案,SSL_1458【数字金字塔】