Springboot 自动装配

注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.2.12.RELEASE进行学习分析,版本不同可能会有细微差别。

1. 前言

关于配置文件可以配置的内容,在Spring Boot官方网站已经提供了完整的配置和解释。

可以这么说,Spring Boot的一大精髓就是自动装配,为开发省去了大量的配置时间。那么Springboot自动装配是怎么实现的呢?

1. @SpringBootApplication

跟着Spring Boot启动类的注解@SpringBootApplication进行源码跟踪,可以找到自动装配的原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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 {
//.........省略.......
}

可以看到@SpringBootApplication是个组合注解,其中包括以下:

  • @SpringBootConfiguration: 用于加载配置文件

  • @EnableAutoConfiguration: 开启自动装配

  • @ComponentScan: 开启注解扫描

2. @EnableAutoConfiguration

继续跟踪,查看@EnableAutoConfiguration源码,里面比较重要的是@Import,导入一个翻译名为自动装配的选择器的类,这个类起始就是自动装配的加载选择器。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

Class<?>[] exclude() default {};

String[] excludeName() default {};
}

继续跟踪AutoConfigurationImportSelector.class,在这个类中有一个重要的方法getCandidateConfigurations,用于加载Springboot配置的自动装配类。

方法getAutoConfigurationEntry会筛选出有效的自动装配类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
//通过读取第三方jar包中的spring.factories文件, 加载所有第三方包中的配置文件,然后存入到IOC容器中
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//这个是关键,通过getCandidateConfigurations方法(下面有)读取所有的spring.factories
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}

//SpringFactoriesLoader去加载所有的spring.factories文件
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//告诉spring读取spring.factories配置文件的key值 getSpringFactoriesLoaderFactoryClass(),下面有源码
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}

在方法getCandidateConfigurations中,通过SpringFactoriesLoader.loadFactoryNames(xxx)去加载配置信息。 它通过SPI机制去加载所有META-INF/spring.factories配置信息。

随便找一个spring.factories配置信息如下:

3. @EnableConfigurationProperties

在上面的 debug 里,我们看到了成功加载的自动装配,目前只看到了配置类,却还没有发现自动装配值,随便选择一个 AutoConfiguration 查看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration(
proxyBeanMethods = false
)
@AutoConfigureOrder(-2147483648)
//判断当前项目有没有这个类
//CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
//Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果
//满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
//.......省略.......
}

需要注意的是 @EnableConfigurationProperties(ServerProperties.class), 他的意思是启动指定类的 ConfigurationProperties 功能;将配置文件中对应的值和 ServerProperties 绑定起来;并把 ServerProperties 加入到 IOC 容器中。

再来看一下ServerProperties

1
2
3
4
5
6
7
8
//软绑定,前缀为server 绑定属性映射文件中的 server 开头的属性
@ConfigurationProperties(
prefix = "server",
ignoreUnknownFields = true
)
public class ServerProperties {
//.......省略.......
}

4. 总结

  1. SpringBoot 启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration 。

  2. @EnableAutoConfiguration 给容器导入 META-INF/spring.factories 里定义的自动配置类。

  3. 筛选有效的自动配置类。

  4. 每一个自动配置类结合对应的 xxxProperties.java 读取配置文件进行自动配置功能 。

5. 附录

@Conditional 扩展注解 作用(判断是否满足当前指定条件)
@ConditionalOnJava 系统的 java 版本是否符合要求
@ConditionalOnBean 容器中存在指定 Bean
@ConditionalOnMissingBean 容器中不存在指定 Bean
@ConditionalOnExpression 满足 SpEL 表达式指定
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissingClass 系统中没有指定的类
@ConditionalOnSingleCandidate 容器中只有一个指定的 Bean,或者这个 Bean 是首选 Bean
@ConditionalOnProperty 系统中指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定资源文件
@ConditionalOnWebApplication 当前是 web 环境
@ConditionalOnNotWebApplication 当前不是 web 环境
@ConditionalOnJndi JNDI 存在指定项