SpringBoot 自动配置原理
application.properties
配置是如何在 Spring Boot 项目中生效的呢?
扫描 spring.factories
文件
Spring Boot 关于自动配置的源码在spring-boot-autoconfigure-x.x.x.x.jar
中。@SpringBootApplication
引用了 @EnableAutoConfiguration
:
@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
引入了 AutoConfigurationImportSelector.class
:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
}
AutoConfigurationImportSelector
的 selectImports
方法通过 SpringFactoriesLoader.loadFactoryNames()
扫描所有具有 META-INF/spring.factories
的 jar 包。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
// AutoConfigurationImportSelector.java
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
return configurations;
}
这个 spring.factories
文件是一组一组的 key=value
的形式,其中一个 key 是 org.springframework.boot.autoconfigure.EnableAutoConfiguration
,这个 key 对应的 value 是以逗号分隔的各种配置类的全称:
找到所有这些配置类后,会将这些自动配置类加载到 Spring 容器中。
自动配置生效
每一个 XxxxAutoConfiguration 自动配置类都是在某些条件之下才会生效的,这些条件的限制在 Spring Boot 中以注解的形式体现。以 ServletWebServerFactoryAutoConfiguration
为例,它上面有一些 ConditionalOnClass
、ConditionalOnWebApplication
等条件,这个配置才会生效:
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@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
,在这个类中其通过 ConfigurationProperties
从配置文件中读取 server.port
的值,然后绑定到 ServerProperties
上,并通过 EnableConfigurationProperties
导入到 Spring 容器中:
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
private Integer port;
private InetAddress address;
}
总结
Spring Boot 启动的时候会通过 @EnableAutoConfiguration
注解找到 META-INF/spring.factories
配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以 AutoConfiguration
结尾来命名的,它实际上就是一个JavaConfig
形式的 Spring 容器配置类,它能通过以 Properties
结尾命名的类中取得在全局配置文件中配置的属性如:server.port
,而 XxxxProperties 类是通过 @ConfigurationProperties
注解与全局配置文件中对应的属性进行绑定的。