【SpringBoot】spring-boot-自動配置原理
思考:配置檔案到底能寫什麼?怎麼寫?自動配置原理;
1.自動配置原理
springboot自動配置主演通過@EnableConfiguration,@Conditional,@EnableConfigurationProperties或者@ConfiguraionProperties幾個註解來進行自動配置。@EnableConfiguraqtion開啟自動配置,主要作用就是呼叫core包裡的loadFactoryNames(),將autoconfig包裡已經寫好的自動配置載入進來。
@Conditional條件註解,通過判斷類路徑下有沒有相應配置的jar包來確定是否載入和自動配置這個類,
@EnableConfigurationProperties的作用就是,給自動配置提供具體的配置引數,只需要寫在application.proerties中,就可以通過對映寫入配置類 的Pojo屬性中。
1)SpringBoot啟動的時候載入主配置類,開啟自動配置功能@EnableAutoConfiguration
2)@EnableAutoConfiguration,利用EnableAutoConfigurationImportSelector給容器中匯入一些元件。
3)從啟動類的@SpringBootApplication進入,在裡面找到@EnableAutoConfiguration
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@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 {
4)EnableAutoConfigutation裡通過@Import匯入了EnableAutoConfigurationImportSelector
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
5)進入EnableAutoConfigurationImportSelector的父類AutoConfigurationImportSelector,找到了selectImports()方法,呼叫了getCandidateConfigurations()方法,在這裡又呼叫了Spring Core包中的loadFactoryNames()方法。這個方法的作用,掃描所有jar包類路徑下 META-INF/spring.factories 把掃描到的這些檔案的內容包裝成properties物件,從properties中獲取到EnableAutoConfig.class類(類名)對應的值,然後把他們新增到容器中
此方法找到getCandidateConfigurations()方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
此方法找到loadFactoryNames()方法
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()方法,裡面有一個常量FACTORIES_RESOURCE_LOCATION,他的值就是"META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
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 ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
6)jar檔案在org.springframework.boot.autoconfigure的spring.factories
7)將類路徑下META-INF/spring.factories裡面配置的所有EnableAutoConfiguration的值加入到了容器中;
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
每一個這樣的xxxAutoConfiguration類都是容器中的一個元件,都加入到容器中;用他們來做自動配置;每一個自動配置類進行自動配置功能。
2.以HttpEncodingAutoConfiguration(http編碼)為例解釋自動配置
1)
@Configuration //表示這是一個配置類,以前編寫的配置檔案一樣,也可以給容器中新增元件
@EnableConfigurationProperties(HttpEncodingProperties.class)//啟動指
定類的ConfigurationProperties功能;將配置檔案中對應的值和HttpEncodingProperties加入到ioc容器中
@ConditionalOnWebApplication//Spring底層@Conditional註解(spring註解版),根據不同的條件,
如果滿足指定的條件,整個配置類裡的配置就回生效;判斷當前應用是否是web應用,如果是,當前配置類生效
@ConditionalOnClass(CharacterEncodingFilter.class)//判斷當前專案有沒有
這個類CharacterEncodingFilter,SpringMVC進行亂碼解決的過濾器
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)//判斷配置檔案中是否存在某個配置 spring.http.encoding;如果不存在,
判斷也是成立的,即使我們配置檔案中不配置spring.http.encoding.enable=true,也是預設生效的
public class HttpEncodingAutoConfiguration {
//他已經和SpringBoot的配置檔案映射了
private final HttpEncodingProperties properties;
//
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
//給容器中新增一個元件,這些元件的值需要從properties中獲取
@Bean
@ConditionalOnMissingBean(CharacterEncodingFilter.class)
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
根據不同的條件判斷,決定這個配置類是否生效?
一旦這個類配置生效;這個配置類就會給容器中新增各種元件;這些元件的屬性是從對應的properties類中獲取的,這些類裡面的每一個屬性又是配置檔案繫結的;
2)所有在配置檔案中能配置的屬性都是在xxx.Properties類中封裝著;配置檔案能配置什麼就可以參照某個功能對應的這個屬性類
@ConfigurationProperties(prefix = "spring.http.encoding")//從配置檔案中獲取指定的值和 //bean的屬性進行繫結
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
}
3.總結
1)SpringBoot啟動會載入大量的自動配置類;
2)我們看我們需要的功能有沒有SpringBoot預設寫好的自動配置類;
3)我們再來看這個配置類到底配置了哪些元件;(只要我們用的元件有,我們就不需要再來配置了);
4)給容器中自動配置類新增元件的時候,會從 properties類中獲取某些屬性。我們就可以在配置檔案中指定這些屬性的值;
xxxConfiguration:自動配置類;給容器中新增元件
xxxProperties封裝配置檔案中相關屬性