源碼學習系列之SpringBoot自動配置(篇二)之HttpEncodingAutoConfiguration 源碼分析
繼上一篇博客源碼學習系列之SpringBoot自動配置(篇一)之后,本博客繼續跟一下SpringBoot的自動配置源碼
ok,先復習一下上一篇的內容,從前面的學習,我們知道了SpringBoot的自動配置主要是由一個選擇器AutoConfigurationImportSelector,先通過選擇器將自動配置的類加載到Spring容器
注意點:
List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
獲取的候選配置的類名- 由SpringFactoriesLoader加載器負責加載配置類名,已經裝載配置類到容器,SpringFactoriesLoader的loadSpringFactories方法讀取自動配置工程的META-INF/spring.factories配置文件,加載配置類的全類名,包裝成Properties對象,然后再加載到容器里
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
/* 將spring.factories的類都裝載到Spring容器*/
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
//將META-INF/spring.factories文件里配置的屬性都裝載到Enumeration數據結構里
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
//遍歷獲取屬性,然后再獲取對應的配置類全類名
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
} catch (IOException var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
}
}
ok,Springboot的自動配置類都在這個包里,源碼很多,所以本博客只是簡單跟一下源碼
自動配置可以說是SpringBoot框架的一個很重要的功能,其強大的功能就是通過很多配置類實現的,當然這么多配置,可以參考SpringBoot官方的配置參考:
https://docs.spring.io/spring-boot/docs/2.1.10.RELEASE/reference/html/common-application-properties.html
SpringBoot的自動配置類很多,顯然不是每個配置類都生效的,比如你沒引對應的jar,那對應的配置類肯定是不起效的,ok,本博客以HttpEncodingAutoConfiguration自動編碼配置類為實例,記錄一下SpringBoot的自動配置
先補充一些@Conditional注解的用法:詳情可以參考我上篇博客SpringBoot系列之@Conditional注解用法簡介
@Conditional派生注解 | 作用(都是判斷是否符合指定的條件) |
---|---|
@ConditionalOnJava | 系統的java版本是否符合要求 |
@ConditionalOnBean | 有指定的Bean類 |
@ConditionalOnMissingBean | 沒有指定的bean類 |
@ConditionalOnExpression | 符合指定的SpEL表達式 |
@ConditionalOnClass | 有指定的類 |
@ConditionalOnMissingClass | 沒有指定的類 |
@ConditionalOnSingleCandidate | 容器只有一個指定的bean,或者這個bean是首選bean |
@ConditionalOnProperty | 指定的property屬性有指定的值 |
@ConditionalOnResource | 路徑下存在指定的資源 |
@ConditionalOnWebApplication | 系統環境是web環境 |
@ConditionalOnNotWebApplication | 系統環境不是web環境 |
@ConditionalOnjndi | JNDI存在指定的項 |
通過上篇博客的學習,我們已經知道了SpringBoot有很多自動配置類,所以本博客拿HttpEncodingAutoConfiguration類來看看
補充:
- @Configuration proxyBeanMethods屬性:默認是開啟的,開啟后允許其它配置類調用這個類的@bean方法,詳情參看Spring官方文檔:Spring官方文檔
package org.springframework.boot.autoconfigure.web.servlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.http.HttpProperties;
import org.springframework.boot.autoconfigure.http.HttpProperties.Encoding;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.filter.CharacterEncodingFilter;
@Configuration(
proxyBeanMethods = false
)//指定是一個配置列,關了proxyBeanMethods,其它配置類就不能調相應的@bean類
@EnableConfigurationProperties({HttpProperties.class})//讓使用 @ConfigurationProperties注解的HttpProperties類生效,HttpProperties類通過ConfigurationProperties注解,將屬性配置一個一個加載進來
@ConditionalOnWebApplication(
type = Type.SERVLET
)//指定系統環境是Web環境配置才起效,並且指定類型是SERVLET
@ConditionalOnClass({CharacterEncodingFilter.class})//系統有CharacterEncodingFilter過濾器類,則配置類起效,CharacterEncodingFilter類:SpringMVC中進行亂碼解決的過濾器
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)//判斷配置文件是否有spring.http.encoding.enabled屬性,如果沒配置,也是默認為true的,因為配置了`matchIfMissing =true`
public class HttpEncodingAutoConfiguration {
//創建一個Encoding對象
private final Encoding properties;
// 構造函數里通過properties.getEncoding();進行屬性映射,獲取默認的配置
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean
@ConditionalOnMissingBean//如果系統沒有CharacterEncodingFilter類,就執行characterEncodingFilter方法
public CharacterEncodingFilter characterEncodingFilter() {
//重新創建一個編碼過濾器
OrderedCharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
//設置默認的配置
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}
@Bean
public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
}
private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final Encoding properties;
LocaleCharsetMappingsCustomizer(Encoding properties) {
this.properties = properties;
}
public void customize(ConfigurableServletWebServerFactory factory) {
if(this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}
}
public int getOrder() {
return 0;
}
}
}
通過對HttpEncodingAutoConfiguration源碼的學習,可以看出,其實主要是用@Conditional及其派生注解,這些注解都是要在特定情況才會起效,起效了,才會將組件加載到Spring容器里
ok,然后我們怎么知道哪些配置是起效的?在SpringBoot項目里,是可以通過配置,開啟打印的,可以在application.properties加上debug=true
屬性就可以
控制台打印的Positive matches就表示有效的配置類
console打印的Negative matches表示不起效的配置類:
比如我的項目沒有加aop的,aop自動配置類就不起效