SpringBoot自动配置源码
springboot启动加载主配置类,@SpringBootApplication下的@EnableAutoConfiguration注解开启自动配置
SpringBoot在启动的时候,从类路径下的META-INF/spring.factories中获取EnableAutoConfigurationn下的xxxAutoConfiguration值,将这些值作为自动配置类导入到容器中,用他们做自动配置,每一个自动配置类进行自动配置功能
@EnableAutoConfiguration:
利用@Import(AutoConfigurationImportSelector.class)给容器中导入组件
- AutoConfigurationImportSelector类中getCandidateConfigurations()方法
//获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//扫描所有jar包类路径下 META-INF/spring.factories
//getSpringFactoriesLoaderFactoryClass()获取 EnableAutoConfiguration.class类
List<String> 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;
}
- 扫描到META-INF/spring.factories后,在SpringFactoriesLoader类中loadSpringFactories()方法
//把扫描到的spring.factories内容包装成properties对象
//从 properties中获取到 EnableAutoConfiguration.class类对应的值,添加到容器中
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
- EnableAutoConfiguration对应的值:每一个xxxAutoConfiguration都是容器的一个组件
以HttpEncodingAutoConfiguration为例:
根据当前不同的条件判断,决定这个配置类是否生效。 一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的xxxproperties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
//表示这是一个配置类,也可以给容器中添加组件
@Configuration
//启用指定类(ServerProperties)的 ConfigurationProperties功能
//所有在配置文件中能配置的属性都在xxxProperties类中封装
ServerProperties类
//从配置文件中获取指定的值,和bean属性进行绑定
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
//定义的属性就是配置文件中可配置的值(server.port=8080)
private Integer port;
//将配置文件的值和 ServerProperties绑定起来,并加入到容器中
@EnableConfigurationProperties(ServerProperties.class)
//Spring底层 @Conditional:根据不同的条件判断配置类是否满足指定条件,从而判断整个配置类是否生效
//判断当前应用是否为 web应用,如果是则当前配置类生效,否则不生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判断当前项目有没有指定类(CharacterEncodingFilter)
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断配置文件中是否存在某个配置(server.servlet.encoding.enabled)
//matchIfMissing = true,不配置就默认为true
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
//已经和配置文件映射过了
private final Encoding properties;
//只有一个有参构造器,将配置文件的properties赋值给此类中定义的properties
//@EnableConfigurationProperties的作用
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
//给容器中添加组件,组件里的某些值需要从properties中获取
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}