SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

系列文章目录和关于我

一丶什么是SpringBoot自动装配

SpringBoot通过SPI的机制,在我们程序员引入一些starter之后,扫描外部引用 jar 包中的 META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,实现引入starter即可开启相关功能的操作,大大简化了程序员手动配置bean,即开即用。

二丶SpringBoot自动装配源码解析

1.源码解析入口

 SpringApplication.run(启动类.class, args)

这是我们最常用的Main方法启动SpringBoot服务的方式,其中启动类上需要标注 @SpringBootApplication注解,自动装配,扫描主类下所有Bean的奥秘就在此

2.@SpringBootApplication注解

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

@SpringBootApplication本身没有什么神奇的地方,重要的是注解上面标注了 @SpringBootConfiguration, @EnableAutoConfiguration,和 @ComponentScan注解

  • @SpringBootConfiguration平平无奇,上面标注了 @Configuration表示标注的类是一个配置类
    SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的
  • @AutoConfigurationPackage其上方标注了 @Import(AutoConfigurationPackages.Registrar.class),加上 @EnableAutoConfiguration上的 @Import(AutoConfigurationImportSelector.class). @Import注解的作用是导入一些bean到Spring容器中,实现此功能的是 ConfigurationClassPostProcessor,它是一个 BeanFactoryPostProcessor会解析配置类中的@Bean,@Import,@ComponentScan等注解
  • @ComponentScan,指导Spring容器需要扫描哪些包下的类加入到Spring容器

也就是说 @SpringBootApplication相当于

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

3.自动配置源码学习前言

SpringBoot并不是Spring的替代品,而是利用Spring加上 约定大于配置的思想,方便程序员开发的框架。所以其底层还是Spring那一套(Spring源码相关博客:Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点

本篇着重研究SpringBoot的自动装配原理,所以一些无关的代码不会进行详细探究,后续会单独学习整理。

4.SpringApplication#run是如何初始化ApplicationContext的

既然SpringBoot是基于Spring的,那么必然是无法脱离ApplicationContext的,接下来我们以 SpringApplication#run为入口看看,SpringApplication是如何初始化一个ApplicationContext的

4.1 SpringApplication的初始化

我们在启动类的main方法中 SpringApplication.run(启动类.class, args)其实最终调用的是

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

在其构造方法中:

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的
* 初始化 ApplicationContextInitializer,和 ApplicationListener 这部分是通过读 META-INF/spring.factories中的内容反射进行初始化,前者是用于在刷新之前初始化 Spring ConfigurableApplicationContext 的回调接口,后者是Spring监听器,后续会进行专门的学习。
* 获取主类 会new出一个 RuntimeException,然后分析 StackTraceElement找到方法名称为 main,然后获取类名

springboot启动源码 自动配置需要关注的部分框出

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

4.2 根据项目创建一个合适的ApplicationConext

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

这里便是通过web应用的类型,反射生成 AnnotationConfigServletWebServerApplicationContext类型的上下文,也就是说,如果当前项目中存在 Servlet,和 ConfigurableWebApplicationContext那么SpringBoot会选择 AnnotationConfigServletWebServerApplicationContext

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

其中 ServletWebServerApplicationContext具备启动Serlvet服务器(如Tomcat)并将Servlet 类型的bean或过滤器类型的 bean 都注册到 Web 服务器的能力

AnnotationConfigServletWebServerApplicationContext则是在 ServletWebServerApplicationContext上增加了根据类路径扫描,注册Component到上下文的能力

4.3 刷新ApplicationContext的前置准备

prepareContext方法中,SpringBoot会把主类注册到Spring容器中,为什么要这么做昵,——主类上的注解 @SpringBootApplication需要 ConfigurationClassPostProcessor解析,才能发挥@Import,@ComponentScan的作用,想要 ConfigurationClassPostProcessor处理主类的前提是主类的BeanDefinition需要在Spring容器中

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

这里的BeanDefinitionRegistry即是 AnnotationConfigServletWebServerApplicationContext中持有的 DefaultListableBeanFactory

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

如果是 CharSequence类型,会尝试使用 Class.forName解析成类,然后尝试使用解析Resouce,解析的Package的方式处理。

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

这里使用 AnnotatedBeanDefinitionReader注册我们的主类,此类在spring源码学习笔记2——基于注解生成BeanDefinition的过程解析中学习过。

简单来说就是会将主类的信息包装成 AnnotatedGenericBeanDefinition,其中会解析 @Scope, @Lazy, @Primary@DependsOn等注解设置到 AnnotatedGenericBeanDefinition中,然后调用 BeanDefinitionCustomizer#customize允许我们自定义处理BeanDefinition。

4.4 刷新ApplicationContext

这里就是调用 AnnotationConfigServletWebServerApplicationContext#refresh方法,来到 AbstractApplicationContextrefresh方法中,执行流程如下

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

这里我们需要注意 调用BeanFactoryPostProcessor,因为这里将调用到 ConfigurationClassPostProcessor,接下来我们将分析其源码,看看它究竟做了什么

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的
4.4.1解析配置类中的相关注解

这一步发生在 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry

4.4.1.1遍历所有的BeanDefinition进行筛选

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

可以看到如果需要处理,会放入到集合中,那么什么样的类才需要进一步处理昵,首先这个类的BeanDefinition需要存在于Spring容器中

  • 具备 @Configuration注解,会被标记为了 full模式
  • 具备 @Component, @ComponentScan, @Import@ImportResource其中任何一个注解,会被标记为 lite模式
  • 具备一个方法标注了 @Bean注解,会被标记为 lite模式

fulllite的区别后面会将

获取候选者后会根据其 @Order注解中的顺序进行排序,SpringBoot项目通常这时候只有主类

4.4.1.2 使用 ConfigurationClassParser 解析候选者

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

在这里SpringBoot的启动类,会被解析,

  1. 首先是进行条件注解解析,如果不符合条件那么什么都不做。这里的条件注解指的是 @Conditional及其复合注解 @ConditionOnClass, @ConditionOnBean
    SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的
  2. 如果标注了 @Component及其复合注解那么 解析内部类 ConfigurationClassParser会把当前配置类中的内部类也当作配置类解析,也就是说如果A是一个配置类候选者,内部类没有@Component,@Configuration也会当做配置进行解析
  3. 解析 @PropertySources@PropertySource ConfigurationClassParser会将 @PropertySources指定的配置,加入到 Environment
  4. 解析 @ComponentScans@ComponentScan 这一步会确认条件注解中的内容满足,然后使用 ComponentScanAnnotationParser,获取指定的路径,如果没有指定任何路径那么使用当前配置所在的路径,这也是为什么SpringBoot主类上没有指定扫描路径,但是默认加载主类所在包下所有类。扫描包路径下的所有类,使用指定的 TypeFilter进行过滤(检查是否具备@Component注解)且条件注解满足才会注册对应的BeanDefinition到容器中,这里SpringBoot指定了 AutoConfigurationExcludeFilter,其作用是排除掉扫描到的自动装配类,因为自动装配类由 @Import(AutoConfigurationImportSelector.class)导入的 AutoConfigurationImportSelector来处理 SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的 扫到的类,还会当前配置类进行解析,如果是一个配置类即满足
    • 具备 @Configuration注解,会被标记为了 full模式
    • 具备 @Component, @ComponentScan, @Import@ImportResource其中任何一个注解,会被标记为 lite模式
    • 具备一个方法标注了 @Bean注解,会被标记为 lite模式 任何一个条件 那么会再次处理,有点递归的意思
  5. 处理 @@Import注解 获取类上面的 @Import注解内容
    • 导入的类是 ImportSelector类型 反射实例化ImportSelector 如果此 ImportSelector实现了 BeanClassLoaderAware, BeanFactoryAwareEnvironmentAware, EnvironmentAware, ResourceLoaderAware会回调对应的方法 调用当前 ImportSelectorselectImports,然后递归执行处理 @Import注解的方法,也就是说可以导入一个具备 @Import的类,如果没有`@Import那么当中配置类解析
    • 导入的类是 ImportBeanDefinitionRegistrar类型 反射实例化 ImportBeanDefinitionRegistrar,然后加入到 importBeanDefinitionRegistrars集合中后续会回调其 registerBeanDefinitions
    • 既不是 ImportBeanDefinitionRegistrar也不是 ImportSelector,将导入的类当做配置类处理,后续会判断条件注解是否满足,然后解析导入的类,并且解析其父类
这一步便会解析到 @Import(AutoConfigurationImportSelector.class)进行自动装配,具体操作后续讲解
  1. 处理 @ImportResource注解 获取注解中指定的路径资源,和指定的 BeanDefinitionReader类型,然后包装到 importedResources集合中,后续回调 BeanDefinitionReader#loadBeanDefinitions(默认使用 XmlBeanDefinitionReader),也就是说我们可以使用 @ImportResource导入一些定义在xml中的bean
  2. 处理标注 @Bean注解的方法 会扫描标注 @Bean的方法,存到 beanMethods集合中,后续解析方法上的条件注解,如果满足条件,将包装成 ConfigurationClassBeanDefinition,其中bean名称和别名来自@Bean中name指定,并且指定其 factoryMethodName,后续实例化bean的时候将反射调用标注的方法生成bean,然后解析 @Lazy, @Primary, @DependsOn等注解,还会解析@Bean注解中标注的是否依赖注入候选者,初始化方法,销毁方法,以及 @Scope注解,然后注册到BeanDefinitionRegistry中
  3. 处理接口中标注@Bean的默认方法 获取当前类实现的全部的接口,且非抽象的方法,然后进行 6.处理标注@Bean 注解的方法
4.4.2增强配置类

ConfigurationClassPostProcessor#postProcessBeanFactory方法中,会对 full模式的配置进行增强,full模式指标注@Configuration注解的类,调用其 enhanceConfigurationClasses方法,拦截 @Bean方法,以确保正确处理 @Bean语义。Spring将使用CGLIB对原配置进行增强,获取增强后的类,替换调用原BeanDefinition记录的类,后续将使用此加强类,这也做的目的在于,调用配置类标注了@Bean方法的时候,不会真正调用其中的逻辑,而是直接取 BeanFactory#getBean中取,保证@Bean标注的方法,产生bean的生命周期完整

4.5 自动装配源码解析

上面关于 ConfigurationClassPostProcessor类的源码解析,我们明白了Spring是如何解析一个配置类的,其中和SpringBoot自动装配关系最密切的是对 @Import注解,SpringBoot启动上标注的 @SpringBootApplication包含了 @Import(AutoConfigurationImportSelector.class),下面我们将解析 AutoConfigurationImportSelector到底做了什么来实现自动装配

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

首先我们可以通过 spring.boot.enableautoconfiguration来设置是否开启自动配置,那怕再配置类上面标注了 @EnableAutoConfiguration也可以进行关闭。

然后会先读取spring-autoconfigure-metadata.properties ,此文件存储的是”待自动装配候选类”过滤的计算规则,会根据里面的规则逐一对候选类进行计算看是否需要被自动装配进容器,并不是全部加载

然后是读取 META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration对应的自动配置类,如

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的
  • 首先使用classLoader读 META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration对应的内容,然后进行去重
  • 然后获取自动装配注解标注的 excludeexcludeName表示不需要进行自动装配的类,并排除掉这些类
  • 然后获取 META-INF/spring.factoriesorg.springframework.boot.autoconfigure.AutoConfigurationImportFilter对应的内容,实例化成 AutoConfigurationImportFilter调用其 match方法,判断这些自动装配类是否需要被过滤掉,这是springboot留给我们的一个扩展点,如果需要读取缓存中的内容进行对自动配置类的过滤,我们可以自己实现一个 AutoConfigurationImportFilter放在 META-INF/spring.factories中,如 org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=com.a.xx即可进行自定义过滤
  • 紧接着会发送一个 AutoConfigurationImportEvent事件 关于SpringBoot的事件会在下一篇中讲解
  • 最后会把需要自动装配的类全限定类名返回,接着就到了 ConfigurationClassPostProcessor中,它会继续使用 ConfigurationClassParser将这些自动配置类进一步解析

有了这些知识,我们可以写一个自己的starter了(脑子:你回了。手:不,我不会)

Original: https://www.cnblogs.com/cuzzz/p/16705188.html
Author: Cuzzz
Title: SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/714289/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球