1. 自动配置原理
配置文件到底能写什么?怎么写?
配置文件能配置的属性参照:
1.1 分析一下具体源码步骤:SpringBoot版本:2.3.1
(1)SpringBoot启动的时候加载主配置类,开启了自动配置功能
@EnableAutoConfiguration
从主方法进行启动,加载了@SpringBootApplication配置类
@SpringBootApplication
public class Day0625Springboot03AutoconfigApplication {
public static void main(String[] args) {
SpringApplication.run(Day0625Springboot03AutoconfigApplication.class, args);
}
}
@SpringBootApplication注解最重要的功能,就是开启了@EnableAutoConfiguration
自动配置。

(2)@EnableAutoConfiguration:开启了自动配置。
分析一下它的作用:
@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 {};
}
@Import({AutoConfigurationImportSelector.class})
@EnableAutoConfiguration利用其中的选择器,
AutoConfigurationImportSelector.class来给spring容器中来导入一些组件。
@Import({AutoConfigurationImportSelector.class})注解的意思就是给
spring容器导一些组件,导哪些组件由AutoConfigurationImportSelector.class
来指定。
(3) 接下来分析AutoConfigurationImportSelector.class
分析一下给容器导入了哪些组件
public String[] selectImports(AnnotationMetadata annotationMetadata)
A:该类中有一个selectImports()方法。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
会得到一个autoConfigurationEntry对象,然后该对象调用getConfigurations()
方法。将得到的值转成的字符串数组返回。
B:点进getAutoConfigurationEntry()方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
其中:这一句代码
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
得到了一个名为configurations的list。
这个方法得到的是所有候选的配置。
C:点进该方法看一下:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
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;
}
注意这一句代码:中的loadFactoryNames方法。
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
D:我们点进去看
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
其中这段代码:
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
返回值中的这个(List)loadSpringFactories(classLoader)方法就在下面

E:loadSpringFactories
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
注意:
发现其中的classLoader.getResources("META-INF/spring.factories")
从类路径下得到一个资源:
扫描所有jar包下类路径下的的这些文件:
META-INF/spring.factories
把这些文件的url得到,放到枚举中:
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
接着:遍历枚举,将其中的url通过loadProperties加载成properties对象。
意味着扫描到的这些文件的内容会被包装成properties对象。
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
在下面会有这些代码:
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
return result;
F:loadSpringFactories()方法返回的result(保含properties信息)
返回给了loadFactoryNames()
G:loadFactoryNames()返回了一个list给了getCandidateConfigurations().
getCandidateConfigurations()只取了返回的list其中的
getSpringFactoriesLoaderFactoryClass()部分。
H:getSpringFactoriesLoaderFactoryClass()是
EnableAutoConfiguration.class
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
从properties中的获取到EnableAutoConfiguration.class类(类名)对应
的值然后把它们添加在容器中。
I:我们查看一下导入的jar包中:
META-INF/spring.factories

我们找到了EnableAutoConfiguration.class类(类名)对应的值。
相当于把这些组件放到了容器中。
小结:将类路径下的 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration
的值添加到容器中。
每一个这样的xxxxAutoConfiguration类都是容器中的一个组件,都加入到
容器中,用他们来做自动配置。
只有这些自动配置类进到容器中,自动配置类注解:
@EnableAutoConfiguration才能起作用。
然后自能配置类实现自动配置功能。
2. 以HttpEncodingAutoConfiguration为例分析自动配置
什么是配置功能?我们以自动配置类为例:
以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理。
1. 点进去里面有几个注解
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
return filter;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
}
A: @Configuration表示这是一个配置类,好比以前编写的配置文件。一般
@Configuration标注的类,其中会有多个@Bean注解标注的方法,可以向容器
中添加组件。@Configuration类似于XML中的<beans/>标签。
B: @EnableConfigurationProperties(ServerProperties.class)启用
指定类ServerProperties.class的ConfigurationProperties功能。
点进这个指定类看一下:
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
private Integer port;
private InetAddress address;
...
}
ServerProperties.class有若干和Http相关属性:
private Integer port
private InetAddress address;
里面有一个@ConfigurationProperties这是我们才学过的:从配置文件中获取指定
的值和ServerProperties类的属性进行绑定注入。
prefix = "server" : 说明要注入的是值,是配置文件中的server键对应的值。
也就是说ServerProperties.class类中属性通过@ConfigurationProperties
注解,和配置文件中的值进行绑定注入。这些属性就是HttpEncodingAutoConfiguration
能配置的信息。
@EnableConfigurationProperties(ServerProperties.class)小结:
HttpEncodingAutoConfiguration配置类通过@EnableConfigurationProperties(ServerProperties.class)
注解,启动指定类ServerProperties.class的功能,再将其中的属性值和HttpEncodingAutoConfiguration绑定起来。
C:@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
第三个注解:其中@Conditional是Spring底层注解,根据不同的条件可以进行条件判
断。如果满足指定的条件,整个配置类里面的配置就会生效。
判断的条件:OnWebApplication,当前应用是不是web应用。
如果是当前HttpEncodingAutoConfiguration配置类生效,否则不生效。
D: @ConditionalOnClass(CharacterEncodingFilter.class)
第四个注解:判断当前项目有没有CharacterEncodingFilter类。显然这是SpringMVC
中解决乱码的过滤器。
E:@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
第五个注解:判断配置文件中是否存在server.servlet.encoding.enabled的配置。
matchIfMissing = true,意思即使不存在该配置也生效。
即使配置文件中不配置server.servlet.encoding.enabled=true,该配置类也默认
生效的。
所以自动配置类HttpEncodingAutoConfiguration根据以上三个不同条件进行判断,决定
这个配置类是否生效。
2. 当自动配置类生效后
A:会将标注了@Bean的方法的返回值注入到IOC中。
private final Encoding properties;
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
@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;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
B:更科学的是,这些注入到IOC中的返回值,是从properties中获取的。而properties
是由第二个注解@EnableConfigurationProperties(ServerProperties.class)来开启的。
这个属性已经和SpringBoot配置文件映射了。
HttpEncodingAutoConfiguration只有一个构造方法:
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
C:那么只有一个构造方法的情况下,参数的值就会从容器中拿。怎么拿到呢?
通过第二个注解:@EnableConfigurationProperties(ServerProperties.class)
它启用了指定类ServerProperties功能,而ServerProperties通过@ConfigurationProperties
注解将自生属性值和SpringBoot配置文件绑定起来。
所以@EnableConfigurationProperties(ServerProperties.class)开启了
ServerProperties类的绑定功能,并将其注入到IOC容器中。
然后HttpEncodingAutoConfiguration自动配置类,就可以从容器中取ServerProperties
类中的Encoding properties传给构造方法的参数。
properties.getServlet().getEncoding();
D:构造方法生效后,继续从Encoding properties中获取如下值。
this.properties.getCharset().name()
this.properties.shouldForce(Encoding.Type.REQUEST)
this.properties.shouldForce(Encoding.Type.RESPONSE)
可以在Encoding类中找到这些对应的属性值:
public Charset getCharset() {
return this.charset;
}
public boolean shouldForce(Encoding.Type type) {
}
3. 整体再看一下HttpEncodingAutoConfiguration自动配置类
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final Encoding properties;
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
@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;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
static class LocaleCharsetMappingsCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final Encoding properties;
LocaleCharsetMappingsCustomizer(Encoding properties) {
this.properties = properties;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
if (this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}
}
@Override
public int getOrder() {
return 0;
}
}
}
1. 首先指明了这是一个配置类:
@Configuration(proxyBeanMethods = false)
2. 开启了一个ServerProperties功能:和绑定配置文件内容
@EnableConfigurationProperties(ServerProperties.class)
能绑定前缀是server的配置文件中的内容
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
所以这个server前缀对应的默认配置,如果我们想改的话,可以在主配置文件中修改。


3. 满足了后面三个注解的条件后,这个配置类就生效了。
给IOC容器中添加一些组建。 @Bean标注的方法的返回值,会被放到容器中。
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
4. 这些返回值的属性是从对应的ServerProperties种获取的。
ServerProperties里的每一个属性有事和配置文件绑定的,那么如果我们
想要修改这些默认配置的属性,就可以在主配置文件中修改。
所以自动配置类添加了哪些组件,这些组件涉及到ServerProperties的哪些属性,就是
我们可以为这个自动配置类在主配置文件配置进行修改的内容。

5. 精髓
SpringBoot启动会加载大量的配置类。
我们看一下我们需要的功能有没有SpringBoot默认写好的自动配置类
我们再来看看这个自动配置类向IOC中添加了哪些组件。(关键点)
如果这些组件满足了我得需求,我就不配了,否则在主配置文件进行配置。
(因为properties类和主配置文件进行了绑定。)
自动配置给容器中类添加组件时,会从properties类中获取某些属性,我们就可以
在主配置文件中指定这些属性的值。
xxxAutoconfiguration:自动配置类,给容器添加组件
xxxProperties: 封装配置文件中的相关属性,组件需要的属性来自xxxProperties
4. @Conditional派生注解
1. @Conditional
(Spring的原生注解@Conditional)
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置类里面的内容才能生效。
2. @Conditional派生注解
SpringBoot中有很多@Conditional的派生注解,我们来看一下:

自动配置类正式在这些@Conditional注解的约束下,有选择的进行自动配置注入。
3. 我们怎么知道哪些自动配置类生效了呢?
只有知道了哪些自动配置类生效了,我们才能知道注入了哪些组件,做了什么事。
我们可以通过在主配置文件配置:
debug=true 属性来开启开启SpringBoot的debug
控制台会打印一份
CONDITIONS EVALUATION REPORT
条件评估报告
显示自动配置类的生效情况。
其中:
Positive matches:是生效的自动配置类
Negative matches:是没有生效的。
