SpringBoot配置方法總結
文章目錄
SpringBoot的一大好處就是:大大簡化了Spring和其他框架和Spring整合時的配置,傳統的SSM套裝雖然很大程度地簡化了Web開發,但是其的配置文件卻較爲繁瑣,爲了簡化配置文件使開發者更專注於業務編碼(懶)可以使用SpringBoot來進行web開發,其精簡的配置和龐大繁茂的生態圈絕對令人驚歎!
SpringBoot之所以可以達到如此精簡的配置,主要原因就是SpringBoot大量的自動配置!!!
自動配置原理:
-
SpringBoot應用從啓動類的main方法中啓動,加載SpringBoot主配置類(依賴@SpringBootApplication註解),主配置類(@SpringBootApplication):
@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 { 。。。 }
-
開啓自動配置功能(依賴**@EnableAutoConfiguration**註解):
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { 。。。 }
-
用選擇器(@Import(AutoConfigurationImportSelector.class))獲取候選配置,給容器導入一些組件:
查看AutoConfigurationImportSelector類的**public String[] selectImports(AnnotationMetadata annotationMetadata)**方法:
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
-
通過selectImports方法間接調用**getCandidateConfigurations(annotationMetadata, attributes)**方法:
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; }
-
再調用SpringFactoriesLoader類的**loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader())**方法去掃描所有jar包類路徑下的:public static final String FACTORIES_RESOURCE_LOCATION = "META-INF /spring.factories";
把掃描到的properties文件的內容包裝成properties對象:
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(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }
從包裝好的properties中獲取到EnableAutoConfiguration.class類(類名)對應的值,然後把它們添加到容器中去。
☆上面這五個步驟大致講述了SpringBoot自動配置的原理,可能還是比較抽象不易理解,接下來用一個例子再次具體分析理解☆
spring.factories中每一個XXXAutoConfiguration類都是容器中的一個組件,都加入到容器中,用他們來做自動配置。每一個XXX自動配置類都可以進行自動配置功能,舉個簡單的例子(HttpEncodingAutoConfiguration):
首先找到HttpEncodingAutoConfiguration類:
//表明這是一個配置類,像編寫配置文件一樣,也可以向容器中添加組件
@Configuration
//啓動指定類的ConfigurationProperties功能,
//將配置文件中對應的值和HttpProperties綁定起來,並將HttpProperties加入Ioc容器
@EnableConfigurationProperties(HttpProperties.class)
//Spring底層的@Conditional註解,根據不同的條件,如果滿足指定的條件
//整個配置類裏面的配置就會生效;判斷當前應用是否是web應用,如果是,則配置類生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判斷當前項目有沒有CharacterEncodingFilter這個類
//SpringMvc中CharacterEncodingFilter這個類一般是配置Web.xml中解決亂碼的過濾器
@ConditionalOnClass(CharacterEncodingFilter.class)
//判斷配置文件中是否存在某個配置spring.http.encoding.enabled
//matchIfMissing = true如果不存在,判斷也成立,
//即使配置文件中不配置spring.http.encoding.enabled=true,也是默認生效的
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
//他的值已經和配置文件映射了
private final HttpProperties.Encoding properties;
//只有一個有參構構造器的情況下,參數的值就會從容器中拿
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean//給容器添加一個組件,這些組件的某些值需要從Properties中獲取
@ConditionalOnMissingBean
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類中獲取的,properties類裏面的每一個屬性又是和配置文件綁定的。
來看一看HttpProperties類:
@ConfigurationProperties(prefix = "spring.http")//從配置文件中獲取指定的值和bean的屬性進行綁定
public class HttpProperties {
private boolean logRequestDetails;
private final Encoding encoding = new Encoding();
//getter/setter/is等等方法。。。
public static class Encoding {//這裏有一個Encoding靜態內部類
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private Charset charset = DEFAULT_CHARSET;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
private Map<Locale, Charset> mapping;
//getter/setter/is等等方法。。。
}
}
這時在我們的application.properties文件中加一條配置:
spring.http.encoding.charset=utf-8
ctrl左鍵這個Key,發現居然跳到了上面HttpProperties類的Encoding靜態內部類的setCharset(Charset charset)方法!!這下子恍然大悟了!當然這只是大量組件中較爲簡單的一個,但是每個組件的自動配置邏輯大同小異,只有掌握了SpringBoot的這一精髓,才能更好對其他的細節進行深入理解!
自定義配置方法
用戶很多時候不可避免的進行自定義配置,SpringBoot除了自動配置,理所當然的支持用戶的自定義配置!
SpringBoot的自定義配置主要有兩種:1.使用配置文件進行外部屬性配置。2.用配置類進行配置。
接下來對兩種配製方法展開說明:
1.使用配置文件進行外部屬性配置:
SpringBoot中比較常見且推薦的是**.properties和.yml**兩種配置文件。
想知道配置文件中所有可配屬性,可以在SpringBoot官方說明文檔中找到 Common application properties——SpringBoot2.1.8
當然如果你已經理解了自動配置的原理,大可不必因爲這點小事去查詢文檔了
YAML是JSON的一個超集,可以將外部配置以層次結構形式存儲起來。當項目的類路徑中用SnakeYAML庫(spring-boot-starter中已經被包含)時,SpringApplication類將自動支持YAML作爲properties的替代。所以在優先級上YAML>properties。
YAML的數據格式和JSON很像,都是樹狀結構都是K-V格式,並通過“:”進行賦值。
properties文件中以".“進行分割的,在yml文件中用”:“進行分割的。yml文件的每個”:"後面一定都要加一個空格,否則文件會報錯。
@Value和@ConfigurationProperties:
可以在編寫代碼時在屬性上使用@Value($(key))來取值並對被註解的屬性賦值,也可以在類上使用@ConfigurationProperties(prefix=“xxx”)註解取出所有以xxx爲前綴的key所對應的值來對被註解類的屬性進行匹配並賦值。
功能 | @ConfigurationProperties | @Value |
---|---|---|
鬆散綁定(鬆散語法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303數據校驗 | 支持 | 不支持 |
複雜類型封裝 | 支持 | 不支持 |
無論是yml文件還是properties文件,這兩個註解都能獲取到值。
如果只是某個業務邏輯中需要獲取一下配置文件中的某個值,建議使用@Value。
如果是編寫一個JavaBean和配置文件進行映射,建議直接用@ConfigurationProperties。
@PropertySesource和@ImportResource:
上面介紹的兩個註解都是默認從默認全局配置文件(application.properties或application.yml)中讀取值,如果需要加載指定配置文件中的值,則需要使用**@PropertySesource(value = {“classpath:xxx.propertiex”},…)**來指定需要加載的一個或多個配置文件。
在使用springboot的時候一般是極少需要添加配置文件的(application.properties除外),但是在實際應用中也會存在不得不添加配置文件的情況,例如集成其他框架或者需要配置一些中間件等,在這種情況下,我們就需要引入我們自定義的xml配置文件了。在SpringBoot中依然支持xml文件的配置方式。但是需要在啓動類上加**@ImportResource(locations = {“classpath:xxx.xml”},…)**註解來指定一個或多個xml文件的位置從而對框架或中間件進行配置。
2.用配置類進行配置:
WebMvcConfigurationSupport
SpringBoot2.0之後建議自定義配置類繼承WebMvcConfigurationSupport,在自定義配置類上加上**@Component**註解就可以自動掃描到配置類並加載了。
WebMvcConfigurationSupport這個類中提供了很多很多方法大多數方法都是顧名思義:處理器異常解析器、添加攔截器等等。。。很多很便利的方法。
@Component
public class Configuration extends WebMvcConfigurationSupport {
//添加自定義攔截器
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserInterceptor()).addPathPatterns("/**").excludePathPatterns("/login","/toLogin");
super.addInterceptors(registry);
}
//自定義視圖解析器,使用這個配置類之後properties文件中配置的視圖解析器就失效了
@Bean
public ViewResolver getViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
}
@Bean
可以對配置類中的方法進行註解,將方法的返回值添加到容器中,容器中這個組件默認的id就是方法名。
@Bean
public UserService userService01(){//容器中就會加入一個UserService類型的組件,id是userService01
return new UserService();
}