springboot的自動配置流程:
主程序啟動會掃描@SpringBootApplication
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
@SpringBootApplication元信息
起到主要作用的是@SpringBootConfiguration和@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 {
@SpringBootConfiguration:
其實就是@Configuration污它,標注當前類為主配置類(主容器)衫贬,當前類指的HelloWorldMainApplication
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@EnableAutoConfiguration:
包含兩個重要注解,@AutoConfigurationPackage和@Import({EnableAutoConfigurationImportSelector.class})
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage的作用:相當于包掃描器缴守,掃描主配置類所在包下的所有自定義的bean,例如@Controller摊聋、@Service等注解標識的bean麻裁。
它的內部是通過@Import({Registrar.class})煎源,利用ImportBeanDefinitionRegistrar的registerBeanDefinitions方法手销,向BeanDefinitionRegistry注冊主配置類所在包下自定義的邏輯組件
@Import({EnableAutoConfigurationImportSelector.class})的作用:通過ImportSelector選擇器執(zhí)行selectImports方法獲得最終啟用(符合當前場景)的自動配置類的全類名的String數(shù)組锋拖,主配置類就會把這些自動配置類的bean加載進spring容器兽埃,@Configuration其實也是spring容器的組件柄错,內部@Component修飾
selectImports方法內部:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
configurations = this.sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return (String[])configurations.toArray(new String[configurations.size()]);
} catch (IOException var6) {
throw new IllegalStateException(var6);
}
}
}
自動配置的核心就在于selectImports方法,下面一步步的剖析這個方法
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
}
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.jms.artemis.ArtemisXAConnectionFactoryConfiguration=
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while(urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource((URL)urls.nextElement())));
}
return loadMetadata(properties);
} catch (IOException var4) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", var4);
}
}
loadMetadata方法內部遍歷應用下的所有jar,加載了spring-boot-autoconfigure-1.5.9.RELEASE.jar下的META-INF/spring-autoconfigure-metadata.properties恒削,配置文件里面的信息為自動配置類xxxAutoConfiguration的@ConditionalOnClass蔓同、@AutoConfigureAfter蹲诀、@AutoConfigureOrder等注解參數(shù)的key-value信息则北,因為自動配置類是否啟用要看符不符合這些條件痕慢,如果啟用掖举,還要對這些自動配置類的加載順序進行排序方篮,返回的autoConfigurationMetadata 封裝了這些信息的Properties励负,Properties把spring-autoconfigure-metadata.properties封裝了
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
參數(shù)annotationMetadata為主啟動類的信息(以及它的注解及元注解信息)巾表,返回的attributes為@EnableAutoConfiguration注解的exclude和excludeName的值
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
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);
}
}
只截取一部分,org.springframework.boot.autoconfigure.EnableAutoConfiguration為key
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,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
getCandidateConfigurations方法返回所有自動配置類的全類名集合(從META-INF/spring.factories原封不動地拿),參數(shù)getSpringFactoriesLoaderFactoryClass()其實就EnableAutoConfiguration.class類對象偶妖,loadFactoryNames方法內部會讀取該應用所有jar包下的META-INF/spring.factories趾访,(其實只有spring-boot-autoconfigure-1.5.9.RELEASE.jar包下有)取出factories文件中key為EnableAutoConfiguration全類名的值的集合扼鞋,裝進集合configurations 返回
configurations = this.removeDuplicates(configurations);
返回原集合云头,不必理會
configurations = this.sort(configurations, autoConfigurationMetadata);
對集合排序溃槐,根據(jù)autoConfigurationMetadata里面的AutoConfigureOrder猴鲫、AutoConfigureAfter谣殊、AutoConfigureBefore等信息對自動配置類集合configurations排序拂共,自動配置類加載是有順序的,
例如WebMvcAutoConfiguration姻几,就必須等到DispatcherServletAutoConfiguration加載成功之后再加載宜狐,而且還要根據(jù)@Order的派生注解@AutoConfigureOrder決定加載優(yōu)先級
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
處理需要排除的自動配置類,不必理會
configurations = this.filter(configurations, autoConfigurationMetadata);
根據(jù)@ConditionalOnClass(類.Class)蛇捌、@ConditionalOnMissingBean(類.Class)等注解肌厨,判斷注解中的類.Class是否存在或不存在,從而篩選出符合啟用的自動配置類豁陆。
例如我們的應用沒有導入redis場景的starter柑爸,而RedisAutoConfiguration里
@Configuration
@ConditionalOnClass({JedisConnection.class, RedisOperations.class, Jedis.class})
@EnableConfigurationProperties({RedisProperties.class})
public class RedisAutoConfiguration {
如果我們應用里沒有這三個JedisConnection.class, RedisOperations.class, Jedis.class,那么RedisAutoConfiguration就不會生效盒音,filter方法會把它過濾掉譬圣,最終返回的configurations就是經(jīng)過過濾后的维哈、符合我們當前應用場景的自動配置類的String數(shù)組飘庄。
這些自動配置類在被@Import進主容器之后碾盐,spring創(chuàng)建這些自動配置類的bean,這些bean各自完成初始化自己內部組件的工作
至此,springboot自動配置類的默認初始化完成
springboot修改默認配置:
springboot為我們自動配置了各個場景下的所有默認配置,那它是如何設置的默認配置呢,再者如果我們需要修改默認配置,應該從哪入手饱溢?
首先要明確一點肋杖,每個自動配置類的主要功能就是給容器中加入所需的組件
以HttpEncodingAutoConfiguration為例
例如HttpEncodingAutoConfiguration類給容器中加入一個解決編碼的CharacterEncodingFilter組件
HttpEncodingAutoConfiguration類信息如下:
@Configuration //表示這是一個配置類津畸,以前編寫的配置文件一樣,也可以給容器中添加組件
@EnableConfigurationProperties(HttpEncodingProperties.class) //啟動指定類的 ConfigurationProperties功能;將配置文件中對應的值和HttpEncodingProperties綁定起來;并把 HttpEncodingProperties加入到ioc容器中,以便自動配置類以有參構造器的方式注入Properties
@ConditionalOnWebApplication //Spring底層@Conditional注解(Spring注解版)纸巷,根據(jù)不同的條件,如果 滿足指定的條件,整個配置類里面的配置就會生效; 判斷當前應用是否是web應用,如果是,當前配置類生效
@ConditionalOnClass(CharacterEncodingFilter.class) //判斷當前項目有沒有這個類,自動配置類是否啟動要看這個注解; CharacterEncodingFilter;SpringMVC中進行亂碼解決的過濾器;
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) //判斷配置文件中是否存在某個配置 spring.http.encoding.enabled;如果不存在,判斷也是成立的 //即使我們配置文件中不配置pring.http.encoding.enabled=true,也是默認生效的;
public class HttpEncodingAutoConfiguration {
private final HttpEncodingProperties properties;
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
@Bean //給容器中添加一個組件,這個組件的某些屬性值需要從properties中獲取
@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;
}
}
我們只需要指定HttpEncodingProperties類中的charset屬性的值即可盾致,而HttpEncodingProperties通過@ConfigurationProperties(prefix = "")又和配置文件綁定了护赊,
這里說的配置文件是我們應用的配置文件判耕,因為spring-boot-autoconfigure-1.5.9.RELEASE.jar沒有配置文件请毛,會掃描我們應用的配置文件仙蚜,
只要對應上prefix前綴和HttpEncodingProperties類的屬性名就行了
HttpEncodingProperties類如下:
可以看出charset的默認值是UTF-8
@ConfigurationProperties(prefix = "spring.http.encoding") //從配置文件(我們自己應用的配置文件)中獲取指定的值和bean的屬性進行綁定
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Charset charset;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
private Map<Locale, Charset> mapping;
public HttpEncodingProperties() {
this.charset = DEFAULT_CHARSET;
}
set/get方法......
}
我們自己在application.properties中自定義配置
spring.http.encoding.enabled=true
spring.http.encoding.charset=gbk
spring.http.encoding.force=true