注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别。
前言
关于配置文件可以配置的内容,在 Spring Boot 官方网站已经提供了完整了配置示例和解释。
可以这么说,Spring Boot 的一大精髓就是自动配置,为开发省去了大量的配置时间,可以更快的融入业务逻辑的开发,那么自动配置是怎么实现的呢?
1. @SpringBootApplication
跟着 Spring Boot 的启动类的注解 @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 {
@EnableAutoConfiguration
开启自动配置。
@ComponentScan
开启注解扫描
从 SpringBootApplication
我们可以发现,这是一个简便的注解配置,它包含了自动配置,配置类,包扫描等一系列功能。
2. @EnableAutoConfiguration
继续跟踪,查看@EnableAutoConfiguration
源码,里面比较重要的是 @Import
,导入了一个翻译名为自动配置的选择器的类。这个类其实就是自动配置的加载选择器。
@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
.用于加载 Spring Boot 配置的自动配置类。
getAutoConfigurationEntry
会筛选出有效的自动配置类。
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), 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;
}
下图是 DEBUG 模式下筛选之后的结果,因为我只添加了 web 模块,所以只有 web 相关的自动配置。
3. xxxAutoConfiguration 与 xxxProperties
在上面的 debug 里,我们看到了成功加载的自动配置,目前只看到了配置类,却还没有发现自动配置值,随便选择一个 AutoConfiguration
查看源码。
这里选择了 ServletWebServerFactoryAutoConfiguration
.
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
//判断当前项目有没有这个类
//CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass(ServletRequest.class)
//Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果
//满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
需要注意的是 @EnableConfigurationProperties(ServerProperties.class)
.他的意思是启动指定类的
ConfigurationProperties
功能;将配置文件中对应的值和 ServerProperties
绑定起来;并把
ServerProperties
加入到 IOC 容器中。
再来看一下 ServerProperties
.
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
显而易见了,这里使用 ConfigurationProperties 绑定属性映射文件中的 server 开头的属性。结合默认配置
# 路径spring-boot-autoconfigure-2.1.1.RELEASE.jar
# /META-INF/spring-configuration-metadata.json
{
"name": "server.port",
"type": "java.lang.Integer",
"description": "Server HTTP port.",
"sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties",
"defaultValue": 8080
}
达到了自动配置的目的。
4. 自动配置总结
- SpringBoot 启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration 。
- @EnableAutoConfiguration 给容器导入META-INF/spring.factories 里定义的自动配置类。
- 筛选有效的自动配置类。
- 每一个自动配置类结合对应的 xxxProperties.java 读取配置文件进行自动配置功能 。
5. 配置类
通过自动配置,我们发现已经帮我们省去了大量的配置文件的编写,那么在自定义配置的时候,我们是不是需要编写XML呢?Spring boot 尽管可以使用 SpringApplication
XML 文件进行配置,但是我们通常会使用 @Configuration
类进行代替,这也是官方推荐的方式。
5.1 XML配置
定义 helloService Bean.
引入配置。
@ImportResource(value = "classpath:spring-service.xml")
@SpringBootApplication
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
5.2 注解配置
此种方式和上面的XML配置是等效的,也是官方推荐的方式。@Configuration
注解的类(要在扫描的包路径中)会被扫描到。
/**
*
* 配置类,相当于传统Spring 开发中的 xml-> bean的配置
*
* @Author niujinpeng
* @Date 2018/12/7 0:04
*/
@Configuration
public class ServiceConfig {
/**
* 默认添加到容器中的 ID 为方法名(helloService)
*
* @return
*/
@Bean
public HelloService helloService() {
return new HelloService();
}
}
6. 附录
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean; |
@ConditionalOnMissingBean | 容器中不存在指定Bean; |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
文章代码已经上传到 GitHub Spring Boot 自动配置。