SpringBoot2.0 自动配置原理

1. 依赖管理

SpringBoot 最方便的地方就是 自动配置互补配置,而自动配置的前提是有这些东西才能配置,也就是必须要有相应的依赖。

SpringBoot 有各种各样的场景启动器,这些启动器里边根据场景的需求帮我们引入了各种依赖。
所以我们只需要对应场景选择启动器即可完成大多数依赖的引入,而且spring-boot-starter-parent => spring-boot-dependencies 里面定义了依赖相应的m默认版本,但是我们又能够自定义版本。
各种场景启动器都会依赖spring-boot-starter,而spring-boot-starter 又会依赖 spring-boot-autoconfigure,后者就是用于自动配置的。

总之:

  1. 场景启动器里边配置依赖;
  2. 场景启动器所依赖的 spring-boot-autoconfigure 帮助自动配置;
  3. 父项目中的 spring-boot-dependencies 管理依赖的版本

2. 自动配置

自动配置的误区:刚学 springBoot 的时候,认为有 @Component、@Configuration 注解的类,程序不就自动注入了么,还管什么自动配置啊?

这种观点是片面的,上面的自动注入有一个前提,那就是类需要在扫描包的范围之内(也就是@SpringBootApplication主配置类同级及以下的目录),但是如果我们引入其他依赖,即使类上面有 @Component 注解,也无法自动注入,因为它不在扫描包的范围之内。

所以这种情况,就需要借助 springBoot 的自动配置原理,进行辅助,完成自动注入。

2.0 几种自动配置的方式列举

1)上面说到,无法完成自动注入是因为不在扫描包范围之内,那最直接的方法就是把类纳入到扫描范围

使用 @ComponentScan 增加扫描的范围

2)@AutoConfiguration + META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

就是 @AutoConfiguration 配置在类上面,然后在 org.springframework.boot.autoconfigure.AutoConfiguration.imports 配置文件中配置这个类的全限定类名。2.7.0 之前版本也兼容

但是实测,@AutoConfiguration 可以不加,甚至 @Component 也可以不加,只需要 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件配置就行,但如果需要控制加载顺序,则可借助 @AutoConfiguration 或者 @AutoConfigureBefore、@AutoConfigureAfter

对于 2.7.0 之前的 springBoot 是另一种方式:@Configuration + META-INF/spring.factories 文件中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 属性配置全类名(可能意识到需要自动注入的类太多,干脆直接搞一个单独维护自动配置类的文件了:org.springframework.boot.autoconfigure.AutoConfiguration.imports)

3)对于已经有 @AutoConfiguration 注解的类,如果某些类依赖于它(也就是这个类注入了,某些类注入才有意义),或者这个类依赖某些其他类(例如注入的成员变量),这种情况可以借助 @Import 注解

有三种方式:
1.直接在 @Import 的 value 中加入依赖或被依赖的类
2.自定义导入类实现 ImportSelector 接口,并加入到 @Import 的 value 中
3.自定义导入类实现 ImportBeanDefinitionRegistrar 接口,并加入到 @Import 的 value 中
后两种方法,适用于较多的情况。

值得注意的是,如果没有指定顺序(可以使用@Component + @Order), @Import 是按照 value 数组的顺序注入的

4)原生 servlet 注入

@ImportResource 用于指定 xml 位置,用于自动注入

5)对于配置类

这种情况可以使用:@ConfigurationProperties(放在配置类上) + @EnableConfigurationProperties(放在自动注入的类上)

2.1 关于容器注入和属性绑定的几种注解

1) @Configuration + @Bean

@Configuration 标注在配置类上,声明这个类是一个配置类,本质上也是容器中的组件(@Component)。其中一个属性proxyBeanMethods,默认为true,含义是是否需要代理,就是是否为单例,true 代表单例,false 代表不是单例。

@Bean 标注在配置类方法上,用于给容器中注入组件,类型为方法返回值类型,组件名称默认为方法名,也可以自定义组件名。

2)@Import

标注在类上,也是为容器中注入组件,前提是该类也是容器中的一个组件(@Configuration,@Component 等等都实行),属性 value 是一个 Class 数组,用于指定注入哪些类型的组件,组件名和类同名但首字母小写。
通常直接在 @Import 的 value 中指定需要导入的类型即可,但有的时候类型太多或者不固定,就不适合直接写导入的类了,可以用下面两种方法。

a. 借助 ImportSelector

实现 ImportSelector 接口,它有一个 selectImports 方法,用于返回想要导入的容器的全类名字符串数组。使用时只要把这个 Selector 填入 @Import 即可借助 @Import 将这些类注入

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);

    @Nullable
    default Predicate<String> getExclusionFilter() {
        return null;
    }
}
// demo
public class DemoSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {
                Demo.class.getName() // 需要导入的类都加入到这个数组即可
        };
    }
}
b. 借助 ImportBeanDefinitionRegistrar

实现 ImportBeanDefinitionRegistrar,实现 registerBeanDefinitions 方法,利用 registry.registerBeanDefinition() 将想要注入的组件注册,然后将这个 Registar 填入 @Import value 中即可。
ImportSelector 中按照全限定类名注入,中间对于bean没有什么操作空间。相对于实现 ImportSelector,实现 ImportBeanDefinitionRegistrar 稍微麻烦一点,但是注入bean之前可以做更多事。

public interface ImportBeanDefinitionRegistrar {
    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        this.registerBeanDefinitions(importingClassMetadata, registry);
    }

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }
}
//例子
public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //想要住放入的类,需要通过 BeanDefinition 包装一下,
        RootBeanDefinition root = new RootBeanDefinition(Test.class);
        registry.registerBeanDefinition("组件名", root);
    }
}

3)@Conditional

标注与配置类或组件上面(@Configuration, @Bean...),用于条件装配,满足一定条件才注入,


image.png

4)@ImportResource

用于引入原生的配置文件。如 XXX.xml 这种形式的文件,里面注入的组件,SpringBoot 是不能直接获取的,利用这个注解,可以使其生效,属性 locations 是配置文件路径的字符数组。

@ImportResource("classpath:beans.xml")

5)@ConfigurationProperties,@Value

@Value 标注在属性上,从配置文件中读取数据赋值给属性@ConfigurationProperties 标注类上,可以从配置文件中读取信息,绑定到类的属性中,使其生效的方法有两种:

a. 标注在组件上

也就是说这个类不止要被 @ConfigurationProperties 修饰,还要是一个组件(@Configuration,@Component...)。

b. 配合 @EnableConfigurationProperties

在某个配置类上,用 @EnableConfigurationProperties 标注,并把 @ConfigurationProperties 标注的类的 class 填入到 @EnableConfigurationProperties 的属性中即可。
这个注解有两个作用:
1、开启 @ConfigurationProperties 标注类的配置绑定功能
2、把这个类的组件自动注册到容器中

6)@ComponentScan

用来指定 Spring 扫描的包,也就是扫描范围,默认是 @Component 注解所在的同级及以下目录

2.2 自动注入的关键,@SpringBootApplication

//说明@SpringBootApplication也是个配置类
@SpringBootConfiguration
//开启自动配置
@EnableAutoConfiguration
//包扫描
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

@ComponentScan 在不指定扫描路径的情况下,默认扫描标注类的同级及以下的路径。可以通过 @SpringBootApplication 的 scanBasePages 属性自定义扫描路径。

//自动配置包
@AutoConfigurationPackage
//导入一个 Selector
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

导入了一个 AutoConfigurationImportSelector 组件,这个组件会总 "META-INF/spring.factories" 文件中读取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值,里边是各种 XXXAutoConfiguration的全类名。所以它就是用来帮我们导入大量的配置类的。

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

这里又导入了一个 AutoConfigurationPackages.Registrar,这个大致就是为当前项目包下的所有组件,如果配置了 scanBasePackages 就用配置的包,没有配置就用主配置类所在的包。

总结:

@SpringBootApplication 帮我们开启了自动配置和组件导入。
借助 @Import 和 AutoConfigurationImportSelector 导入各种 AutoConfiguration 组件,用于自动配置。
借助 @Import 和 AutoConfigurationPackages.Registrar 导入当前项目包路径中的组件。

3. 自动配置

@SpringBootApplication 从 spring-boot-autoconfigure 中获取并注入各种 AutoConfiguration 组件,用来自动配置,里边会利用 2.1 中的几种注解实现自动配置,互补配制和条件装配。例如:Servlet 容器自动配置的例子解析

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

推荐阅读更多精彩内容