Bootstrap

【JavaEE】Spring Boot 自动装配原理(源码分析)

一. 前言

  • 我们在写Spring Boot的程序代码的时候, 可以注入很多我们没有定义过的Bean.
  • 例如:
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    public DataSourceTransactionManager transactionManager;
    @Autowired
    public AutowireCapableBeanFactory autowireCapableBeanFactory;
  • 这些对象是什么时候注入进去的, Spring 是如何做的,我们可以通过阅读源码来解决这个问题.

二.Spring Boot自动装配的原理分析.

2.1 源码阅读

  • 要搞清楚自动装配的原因, 我们需要去分析Spring Boot的启动类.

  • @SpringBootApplication 标注的类 就是SpringBoot项目的启动类, 这个注解也是SpringBoot实现自动配置的核心
    在这里插入图片描述

  • 元注解:

    • JDK中提供了4个标准的用来对注解类型进行注解的注解类, 我们称之为 meta-annotation(元注
      解)
    • @Target 描述注解的使用范围(即被修饰的注解可以用在什么地方).
    • @Retention 描述注解保留的时间范围.
    • @Documented 描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息
    • @Inherited 使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则
      其子类将自动具有该注解
      )
  • @SpringBootConfiguration 在这里插入图片描述

里面就是@Configuration, 标注当前类为配置类, 其实只是做了一层封装改了个名字而已.
(@Indexed注解,是用来加速应用启动的, 不用关心), 本质上就是将启动类交给Spring进行管理

  • @ComponentScan (包扫描)
    可以通过 basePackageClasses 或 basePackages 来定义要扫描的特定包, 如果没有定义
    特定的包, 将从声明该注解的类的包开始扫描, 这也是为什么SpringBoot项目声明的注解类必须要在启动类的目录下,这样通过@Controller , @Service , @Repository , @Component , @Configuration ,@Bean标注的类/对象,就会被Spring进行管理. excludeFilters 自定义过滤器, 通常用于排除一些类, 注解等

2.2 @EnableAutoConfiguration(自动装配的核心注解)

  • 观察@EnableAutoConfiguration注解的实现:
    在这里插入图片描述
    这个注解包含两部分:

2.2.1@AutoConfigurationPackage

源码如下:
在这里插入图片描述
这个注解主要是导入一个配置文件 AutoConfigurationPackages.Registrar.class
在这里插入图片描述

  • registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry): 这个方法是ImportBeanDefinitionRegistrar接口的一部分,用于在Spring应用程序上下文中注册额外的bean定义。当一个@Configuration类使用了@Import注解并且指定了实现ImportBeanDefinitionRegistrar接口的类时,这个方法会被调用。在这个例子中,它使用PackageImports(一个假定存在的辅助类)来获取包名,并将这些包名作为字符串数组传递给另一个重载的register方法,以便在注册表中注册这些包中的类作为Spring bean定义。

  • determineImports(AnnotationMetadata metadata): 这个方法是DeterminableImports接口的一部分,用于确定哪些类应该被导入到当前的@Configuration类或@SpringBootApplication类中。在这个例子中,它返回了一个单例集合(Set),其中包含了一个PackageImports对象。这个对象封装了应该被扫描的包的信息,这样Spring就能自动发现并注册这些包中的@Component、@Service、@Repository等注解的类。

Registrar实现了 ImportBeanDefinitionRegistrar 类, 就可以被注解@Import导⼊到spring
容器里. (String[])(new PackageImports(metadata)).getPackageNames().toArray(new
String[0]) : 当前启动类所在的包名. 所以:@AutoConfigurationPackage 就是将启动类所在的包下⾯所有的组件都扫描注冊到spring 容器中.

2.2.2 @Import({AutoConfigurationImportSelector.class})

  • 使用@Import注解,导入了实现ImportSelector接口的实现类.
    在这里插入图片描述

  • selectImports() 方法底层调用 getAutoConfigurationEntry() 方法, 获取可自动配置的配置类信息集合.
    在这里插入图片描述

  • getAutoConfigurationEntry() 方法通过调用getCandidateConfigurations(annotationMetadata, attributes) 方法获取在配置文件中配置的所有自动配置类的集合
    在这里插入图片描述

  • getCandidateConfigurations 方法的功能:

    • 获取所有基METAINF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imp
      orts 文件, META-INF/spring.factories 文件中配置类的集合. 在引入的起步依赖中, 通常都有包含以上两个文件
      在这里插入图片描述

这里面包含了很多第三方依赖的配置文件(连续按两下shift可以查看对应的源码)

  1. 在加载自动配置类的时候, 并不是将所有的配置全部加载进来, 而是通过@Conditional等注解的判
    断进行动态加载.@Conditional是spring底层注解, 意思就是根据不同的条件, 来进行自己不同的条件判断,如果满足指定的条件,那么配置类里边的配置才会生效.
  2. META-INF/spring.factories文件是Spring内部提供的一个约定俗成的加载方式, 只需要在模块的
    META-INF/spring.factories文件中配置即可, Spring就会把相应的实现类注入到Spring容器中.
    注: 会加载所有jar包下的classpath路径下的META-INF/spring.factories文件, 这样文件不止一个
  • 比如 Redis的配置: RedisAutoConfiguration
    在这里插入图片描述
    由于我这个项目的pom依赖中,没有加入redis相关的依赖, 所以会根据注解@ConditionalOnMissingBean不进行加载

三.总结.

  • SpringBoot 自动配置原理的大概流程如下:
    在这里插入图片描述
    当SpringBoot程序启动时, 会加载配置文件当中所定义的配置类, 通过 @Import 注解将这些配置类全
    部加载到Spring的IOC容器中, 交给IOC容器管理

  • SpringBoot的自动配置原理源码口是 @SpringBootApplication 注解, 这个注解封装了3个注

    • @SpringBootConfiguration 标志当前类为配置类
    • @ComponentScan 进行包扫描(默认扫描的是启动类所在的当前包及其子包)
    • @EnableAutoConfiguration
      • @Import 注解 : 读取当前项目下所有依赖jar包中 META-INF/spring.factories ,
        METAINF/spring/org.springframework.boot.autoconfigure.AutoConfigurat
        ion.imports 两个文件里面定义的配置类(配置类中定义了 @Bean 注解标识的方法)
      • @AutoConfigurationPackage : 把启动类所在的包下面所有的组件都注入到 Spring
        容器中
;