SpringBoot自动装配原理

Springboot自动装配过程

什么是Springboot自动装配?
我们使用ssm开发的时候,需要写spring.xml或者配置bean,需要配置<bean/>、@Bean把类交给工厂管理。而反观springboot开发的时候,只需要配置一些简单的.properties(.yml)就行了。其实springboot就是把这些我们以前要写的<bean/>、@Bean封装成启动器(starter)导入交给工厂了。

需要了解springboot的自动装配过程,必须从如下几个相关注解开始

  • @SpringBootApplication
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
}

@Target({ElementType.TYPE}):注解可以标记在哪;
@Retention(RetentionPolicy.RUNTIME):注解标注的类编译以什么方式保留;
@Documented:生成文档信息的时候保留注解,对类作辅助说明;
@Inherited:继承关系;
@SpringBootConfiguration:Spring Boot的配置类;
@EnableAutoConfiguration:开启自动配置功能;
@ComponentScan(包路径):扫描包;
excludeFilters:排除策略;

如:@ComponentScan(basePackages = "com.not_lsj",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = {"com.not_lsj.controller.*"})})
扫描 com.not_lsj 包,但是把controller包下所有的类都排除掉,不参与扫描
  • @EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {

}

@AutoConfigurationPackage:将当前配置类所在包保存在BasePackages的Bean中,供Spring内部使用。就是注册了一个bean,这个bean用来保存当前配置类的;
@Import({AutoConfigurationImportSelector.class}):导入当前类;

  • Conditional衍生注解
    作用:条件注解,必须是@Conditional指定的条件成立,才给容器中添加组件,组件才生效。
    列举几种常见的:

@ConditionalOnBean:容器中存在指定Bean
@ConditionalOnMissingBean:容器中不存在指定Bean
@ConditionalOnClass:系统中有指定的类
@ConditionalOnMissingClass:系统中没有指定的类
@ConditionalOnProperty:系统中指定的属性是否有指定的值
@ConditionalOnWebApplication:当前是web环境
......

顺便说一下,spring创建bean的几种方式:

  1. XML解析
  2. 注解(@Configuration、@Component(及其衍生注解)、@Bean)
  3. ClassPathBeanDefinitionScanner扫描特定路径下的具有注解的Bean 进行注册
  4. @import

在此介绍一下@Import注解:作用是导入某个类(一般用于封装)

A.class是一个普通类

  1. @Import(A.class):直接导入某个类

B implements importSelector

  1. @Import(B.class):一次导入多个类
    importSelector有一个子接口 DeferredImportSelector

C implements importBeanDefinitionRegister

  1. @Import(C.class):导入一个定制的类

Springboot就是用的@Import注解实现自动配置,实现了DeferredImportSelector接口

  • DeferredImportSelector

有两大特性

  1. 延迟加载:spring源码中解析@Import注解,都是再其他注解解析完后。
    如:某个Bean在容器中通过@Bean注册了,我们需要定制同一个Bean,这时就需要延迟加载了,因为会通过@ConditionOnBean..校验当前容器是否存在这个Bean,如果存在,我们定制的就不生效。
  2. 分组:当前类导入的Bean,都是与Spring容器里面的Bean分开来的,排序只是在本组内;
    如:当前spring自己也有很多Bean要注册,然后我们通过DeferredImportSelector又导入一组Bean,这两组Bean时互补干扰的,相反如果他们没有分组,会导致容器的与我们定制的不能保证加载顺序,上诉的@ConditionOnBean..校验会存在问题;如果先加载我们定制的Bean,而到容器加载的时候,发现存在这个Bean就会报错。

实现类的方法执行顺序:
先调用getImportGroup()
有返回值:执行返回值的内容 -->process() -->selectImports(),会实现排序
无返回值:执行父类的selectImports()导入Bean

  • 部分实现的源码
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
        Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
            return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
        });
        // getAutoConfigurationEntry进行扫描具有META-INF/spring.factories文件的jar
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
        this.autoConfigurationEntries.add(autoConfigurationEntry);
        Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();

        while(var4.hasNext()) {
            String importClassName = (String)var4.next();
            this.entries.putIfAbsent(importClassName, annotationMetadata);
        }

    }

通过@Import导入一个DeferredImportSelector类型的类:AutoConfigurationImportSelector

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            // 获取EnableAutoConfiguration注解属性信息
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            // 从META‐INF/spring.factories中获得候选的自动配置类(!!!)
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            // 排重
            configurations = this.removeDuplicates(configurations);
            //根据EnableAutoConfiguration注解中属性,获取不需要自动装配的类名单
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            // 根据:@EnableAutoConfiguration.exclude 
            //      @EnableAutoConfiguration.excludeName 
            //      spring.autoconfigure.exclude 进行排除
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            // 通过读取spring.factories 中的OnBeanCondition\OnClassCondition\OnWebApplicationCondition进行过滤
            configurations = this.getConfigurationClassFilter().filter(configurations);
            // 这个方法是调用实现了AutoConfigurationImportListener的bean.. 分别把候选的配置名单,和排除的配置名单传进去做扩展
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }
// 最后排序并注册Bean
 public Iterable<Entry> selectImports() {}

每一个springboot的应用,都会导入一个spring-boot-autoconfigure模块,而META-INF/spring.factories文件就在这个模块下面。而校验自动装配生效的是org.springframework.boot.autoconfigure.EnableAutoConfiguration,如图:

2b91dcce8d716cd5275b90d65472c47.png

说明:在springboot启动类内部执行run时,会执行selectImports()方法,找到对应的配置类的权限定名对应的class,加载到spring容器中。

最后看个例子:上面截图的AopAutoConfiguration

@Configuration(
    proxyBeanMethods = false // @Configuration表示是一个配置类, proxyBeanMethods = false表示当前类不是代理类(默认true)
)
@ConditionalOnProperty(  // 表示当前项目环境必须存在某种配置
    prefix = "spring.aop",
    name = {"auto"},  // 必须spring.aop.auto配置
    havingValue = "true",
    matchIfMissing = true // 如果等于false,不存在spring.aop.auto,aop不生效                     
) // 但是这里等于true表示不存在spring.aop.auto配置,就是用默认的aop
public class AopAutoConfiguration {

}

pom.xml引入xxx-starter,自动装配xxx功能,根据定义的规则校验成功之后,就是将所需的类导入spring工厂管理,完成自动装配。

  • 流程图
    springboot-自动装配.png

总结:

  1. 首先通过@Import导入一个DeferredImportSelector类型的类
  2. 扫描所有jar中的META‐INF/spring.factories
  3. 得到所有的有类路径的Bean组成List进行排序加载
  4. 最后spring就能管理这些类了
  • 自定义启动器(starter)

通过对springboot自动配置原理的了解,只要根据springboot规则,通过org.springframework.boot.autoconfigure.EnableAutoConfiguration导入,就能制定我们自定义的starter:
规则:
启动器(starter)就是空的jar文件,用来提供辅助性依赖管理。
自定义一个spring-boot-autoconfigure的配置模块,写配置类
命名:
官方:spring-boot-starter-xxx
非官方:xxx-spring-boot-starter(mybatis)

实现步骤:

构建一个管理父项目(类是微服务的父项目)
构建xxx-spring-boot-autoconfigure模块实现配置类
构建xxx-spring-boot-starter提供调用

自定义starter案例:https://gitee.com/not-lsj/spring-boot-starter.git

结尾:

  1. 本文介绍了springboot自动装配原理
  2. 模拟实现了自定义starter
  3. 某个时刻会把springboot启动原理总结发布和两篇文章的总结流程图奉上
  4. 欢迎来交流

最后的最后:第一次写总结,可能会不太好,但是会越来越努力!!!

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容