Spring Boot實(shí)戰(zhàn)教程之自動(dòng)配置詳解
前言
大家應(yīng)該都有所了解,隨著Ruby、Groovy等動(dòng)態(tài)語(yǔ)言的流行,相比較之下Java的開(kāi)發(fā)顯得格外笨重。繁多的配置、低下的開(kāi)發(fā)效率、復(fù)雜的部署流程以及第三方技術(shù)集成難度大等問(wèn)題一直被人們所詬病。隨著Spring家族中的新星Spring Boot的誕生,這些問(wèn)題都在逐漸被解決。
個(gè)人覺(jué)得Spring Boot中最重要的兩個(gè)優(yōu)勢(shì)就是可以使用starter簡(jiǎn)化依賴(lài)配置和Spring的自動(dòng)配置。下面這篇文章將給大家詳細(xì)介紹Spring Boot自動(dòng)配置的相關(guān)內(nèi)容,話(huà)不多說(shuō),來(lái)一起看看詳細(xì)的介紹。
使用starter簡(jiǎn)化依賴(lài)配置
Spring提供了一系列starter來(lái)簡(jiǎn)化Maven配置。其核心原理也就是Maven和Gradle的依賴(lài)傳遞方案。當(dāng)我們?cè)谖覀兊膒om文件中增加對(duì)某個(gè)starter的依賴(lài)時(shí),該starter的依賴(lài)也會(huì)自動(dòng)的傳遞性被依賴(lài)進(jìn)來(lái)。而且,很多starter也依賴(lài)了其他的starter。例如web starter就依賴(lài)了tomcat starter,并且大多數(shù)starter基本都依賴(lài)了spring-boot-starter。
Spring自動(dòng)配置
Spring Boot會(huì)根據(jù)類(lèi)路徑中的jar包、類(lèi),為jar包里的類(lèi)自動(dòng)配置,這樣可以極大的減少配置的數(shù)量。簡(jiǎn)單點(diǎn)說(shuō)就是它會(huì)根據(jù)定義在classpath下的類(lèi),自動(dòng)的給你生成一些Bean,并加載到Spring的Context中。自動(dòng)配置充分的利用了spring 4.0的條件化配置特性,能夠自動(dòng)配置特定的Spring bean,用來(lái)啟動(dòng)某項(xiàng)特性。
條件化配置
假設(shè)你希望一個(gè)或多個(gè)bean只有在某種特殊的情況下才需要被創(chuàng)建,比如,一個(gè)應(yīng)用同時(shí)服務(wù)于中美用戶(hù),要在中美部署,有的服務(wù)在美國(guó)集群中需要提供,在中國(guó)集群中就不需要提供。在Spring 4之前,要實(shí)現(xiàn)這種級(jí)別的條件化配置是比較復(fù)雜的,但是,Spring 4引入了一個(gè)新的@Conditional注解可以有效的解決這類(lèi)問(wèn)題。
@Bean
@Conditional(ChinaEnvironmentCondition.class)
public ServiceBean serviceBean(){
return new ServiceBean();
}
當(dāng)@Conditional(ChinaEnvironmentCondition.class)條件的值為true的時(shí)候,該ServiceBean才會(huì)被創(chuàng)建,否則該bean就會(huì)被忽略。
@Conditional指定了一個(gè)條件。他的條件的實(shí)現(xiàn)是一個(gè)Java類(lèi)——ChinaEnvironmentCondition,要實(shí)現(xiàn)以上功能就要定義ChinaEnvironmentCondition類(lèi),并繼承Condition接口并重寫(xiě)其中的matches方法。
class ChinaEnvironmentCondition implements Condition{
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.containProperty("ENV_CN");
}
}
在上面的代碼中,matches方法的內(nèi)容比較簡(jiǎn)單,他通過(guò)給定的ConditionContext對(duì)象進(jìn)而獲取Environment對(duì)象,然后使用該對(duì)象檢查環(huán)境中是否存在ENV_CN屬性。如果存在該方法則直接返回true,反之返回false。當(dāng)該方法返回true的時(shí)候,就符合了@Conditional指定的條件,那么ServiceBean就會(huì)被創(chuàng)建。反之,如果環(huán)境中沒(méi)有這個(gè)屬性,那么這個(gè)ServiceBean就不會(huì)被創(chuàng)建。
除了可以自定義一些條件之外,Spring 4本身提供了很多已有的條件供直接使用,如:
@ConditionalOnBean @ConditionalOnClass @ConditionalOnExpression @ConditionalOnMissingBean @ConditionalOnMissingClass @ConditionalOnNotWebApplication
Spring Boot應(yīng)用的啟動(dòng)入口
自動(dòng)配置充分的利用了spring 4.0的條件化配置特性,那么,Spring Boot是如何實(shí)現(xiàn)自動(dòng)配置的?Spring 4中的條件化配置又是怎么運(yùn)用到Spring Boot中的呢?這要從Spring Boot的啟動(dòng)類(lèi)說(shuō)起。Spring Boot應(yīng)用通常有一個(gè)名為*Application的入口類(lèi),入口類(lèi)中有一個(gè)main方法,這個(gè)方法其實(shí)就是一個(gè)標(biāo)準(zhǔn)的Java應(yīng)用的入口方法。一般在main方法中使用SpringApplication.run()來(lái)啟動(dòng)整個(gè)應(yīng)用。值得注意的是,這個(gè)入口類(lèi)要使用@SpringBootApplication注解聲明。@SpringBootApplication是Spring Boot的核心注解,他是一個(gè)組合注解。
@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 {
// 略
}
@SpringBootApplication是一個(gè)組合注解,它主要包含@SpringBootConfiguration、@EnableAutoConfiguration等幾個(gè)注解。也就是說(shuō)可以直接在啟動(dòng)類(lèi)中使用這些注解來(lái)代替@ SpringBootApplication注解。 關(guān)于Spring Boot中的Spring自動(dòng)化配置主要是@EnableAutoConfiguration的功勞。該注解可以讓Spring Boot根據(jù)類(lèi)路徑中的jar包依賴(lài)為當(dāng)前項(xiàng)目進(jìn)行自動(dòng)配置。
至此,我們知道,Spring Boot的自動(dòng)化配置主要是通過(guò)@EnableAutoConfiguration來(lái)實(shí)現(xiàn)的,因?yàn)槲覀冊(cè)诔绦虻膯?dòng)入口使用了@SpringBootApplication注解,而該注解中組合了@EnableAutoConfiguration注解。所以,在啟動(dòng)類(lèi)上使用@EnableAutoConfiguration注解,就會(huì)開(kāi)啟自動(dòng)配置。
那么,本著刨根問(wèn)底的原則,當(dāng)然要知道@EnableAutoConfiguration又是如何實(shí)現(xiàn)自動(dòng)化配置的,因?yàn)槟壳盀橹梗覀冞€沒(méi)有發(fā)現(xiàn)Spring 4中條件化配置的影子。
EnableAutoConfiguration
其實(shí)Spring框架本身也提供了幾個(gè)名字為@Enable開(kāi)頭的Annotation定義。比如@EnableScheduling、@EnableCaching、@EnableMBeanExport等,@EnableAutoConfiguration的理念和這些注解其實(shí)是一脈相承的。
- @EnableScheduling是通過(guò)@Import將Spring調(diào)度框架相關(guān)的bean定義都加載到IoC容器。
- @EnableMBeanExport是通過(guò)@Import將JMX相關(guān)的bean定義加載到IoC容器。
- @EnableAutoConfiguration也是借助@Import的幫助,將所有符合自動(dòng)配置條件的bean定義加載到IoC容器。
下面是EnableAutoConfiguration注解的源碼:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
//略
}
觀察@EnableAutoConfiguration可以發(fā)現(xiàn),這里Import了@EnableAutoConfigurationImportSelector,這就是Spring Boot自動(dòng)化配置的“始作俑者”。
至此,我們知道,至此,我們知道,由于我們?cè)赟pring Boot的啟動(dòng)類(lèi)上使用了@SpringBootApplication注解,而該注解組合了@EnableAutoConfiguration注解,@EnableAutoConfiguration是自動(dòng)化配置的“始作俑者”,而@EnableAutoConfiguration中Import了@EnableAutoConfigurationImportSelector注解,該注解的內(nèi)部實(shí)現(xiàn)已經(jīng)很接近我們要找的“真相”了。
EnableAutoConfigurationImportSelector
EnableAutoConfigurationImportSelector的源碼在這里就不貼了,感興趣的可以直接去看一下,其實(shí)實(shí)現(xiàn)也比較簡(jiǎn)單,主要就是使用Spring 4 提供的的SpringFactoriesLoader工具類(lèi)。通過(guò)SpringFactoriesLoader.loadFactoryNames()讀取了ClassPath下面的META-INF/spring.factories文件。
這里要簡(jiǎn)單提一下spring.factories文件,它是一個(gè)典型的java properties文件,配置的格式為Key = Value形式。
EnableAutoConfigurationImportSelector通過(guò)讀取spring.factories中的key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的值。如spring-boot-autoconfigure-1.5.1.RELEASE.jar中的spring.factories文件包含以下內(nèi)容:
# 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.webservices.WebServicesAutoConfiguration
上面的EnableAutoConfiguration配置了多個(gè)類(lèi),這些都是Spring Boot中的自動(dòng)配置相關(guān)類(lèi);在啟動(dòng)過(guò)程中會(huì)解析對(duì)應(yīng)類(lèi)配置信息。每個(gè)Configuation都定義了相關(guān)bean的實(shí)例化配置。都說(shuō)明了哪些bean可以被自動(dòng)配置,什么條件下可以自動(dòng)配置,并把這些bean實(shí)例化出來(lái)。
如果我們新定義了一個(gè)starter的話(huà),也要在該starter的jar包中提供 spring.factories文件,并且為其配置org.springframework.boot.autoconfigure.EnableAutoConfiguration對(duì)應(yīng)的配置類(lèi)。
Configuation
我們從spring-boot-autoconfigure-1.5.1.RELEASE.jar中的spring.factories文件隨便找一個(gè)Configuration,看看他是如何自動(dòng)加載bean的。
@Configuration
@AutoConfigureAfter({JmxAutoConfiguration.class})
@ConditionalOnProperty(
prefix = "spring.application.admin",
value = {"enabled"},
havingValue = "true",
matchIfMissing = false
)
public class SpringApplicationAdminJmxAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SpringApplicationAdminMXBeanRegistrar springApplicationAdminRegistrar() throws MalformedObjectNameException {
String jmxName = this.environment.getProperty("spring.application.admin.jmx-name", "org.springframework.boot:type=Admin,name=SpringApplication");
if(this.mbeanExporter != null) {
this.mbeanExporter.addExcludedBean(jmxName);
}
return new SpringApplicationAdminMXBeanRegistrar(jmxName);
}
}
看到上面的代碼,終于找到了我們要找的東西——Spring 4的條件化配置。上面SpringApplicationAdminJmxAutoConfiguration在決定對(duì)哪些bean進(jìn)行自動(dòng)化配置的時(shí)候,使用了兩個(gè)條件注解:ConditionalOnProperty和ConditionalOnMissingBean。只有滿(mǎn)足這種條件的時(shí)候,對(duì)應(yīng)的bean才會(huì)被創(chuàng)建。這樣做的好處是什么?這樣可以保證某些bean在沒(méi)滿(mǎn)足特定條件的情況下就可以不必初始化,避免在bean初始化過(guò)程中由于條件不足,導(dǎo)致應(yīng)用啟動(dòng)失敗。
總結(jié)
至此,我們可以總結(jié)一下Spring Boot的自動(dòng)化配置的實(shí)現(xiàn):

通過(guò)Spring 4的條件配置決定哪些bean可以被配置,將這些條件定義成具體的Configuation,然后將這些Configuation配置到spring.factories文件中,作為key: org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,這時(shí)候,容器在啟動(dòng)的時(shí)候,由于使用了EnableAutoConfiguration注解,該注解Import的EnableAutoConfigurationImportSelector會(huì)去掃描classpath下的所有spring.factories文件,然后進(jìn)行bean的自動(dòng)化配置。
所以,如果我們想要自定義一個(gè)starter的話(huà),可以通過(guò)以上方式將自定義的starter中的bean自動(dòng)化配置到Spring的上下文中,從而避免大量的配置。
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持
相關(guān)文章
Java 日期格式y(tǒng)yyy-MM-dd與YYYY-MM-dd區(qū)別
我們?cè)趈ava中常用的規(guī)范格式為:2023-11-11
yyyy-MM-dd HH:mm:ss:SSS 24小時(shí)制或yyyy-MM-dd hh:mm:ss:SSS 12小時(shí)制,本文就來(lái)介紹一下兩者的區(qū)別,感興趣的可以了解一下
SpringBoot集成Hadoop對(duì)HDFS的文件操作方法
這篇文章主要介紹了SpringBoot集成Hadoop對(duì)HDFS的文件操作方法,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-07-07
Java隨機(jī)生成驗(yàn)證碼的實(shí)現(xiàn)示例
這篇文章主要介紹Java隨機(jī)生成驗(yàn)證碼的實(shí)現(xiàn)方法,文中有相關(guān)的實(shí)現(xiàn)代碼供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下2023-08-08
spring boot+spring cache實(shí)現(xiàn)兩級(jí)緩存(redis+caffeine)
這篇文章主要介紹了spring boot+spring cache實(shí)現(xiàn)兩級(jí)緩存(redis+caffeine),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
mybatisplus的連表增強(qiáng)插件mybatis plus join
本文主要介紹了mybatisplus的連表增強(qiáng)插件mybatis plus join,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
Java開(kāi)發(fā)之普通web項(xiàng)目轉(zhuǎn)為Maven項(xiàng)目的方法
這篇文章主要給大家介紹了關(guān)于Java開(kāi)發(fā)之普通web項(xiàng)目轉(zhuǎn)為Maven項(xiàng)目的相關(guān)資料,文中通過(guò)圖文將轉(zhuǎn)換的方法步驟介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12
JavaFX實(shí)現(xiàn)簡(jiǎn)易時(shí)鐘效果
這篇文章主要為大家詳細(xì)介紹了JavaFX實(shí)現(xiàn)簡(jiǎn)易時(shí)鐘效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
SpringBoot引入Thymeleaf的實(shí)現(xiàn)方法
這篇文章主要介紹了SpringBoot引入Thymeleaf的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Java模擬rank/over函數(shù)實(shí)現(xiàn)獲取分組排名的方法詳解
這篇文章主要為大家詳細(xì)介紹了Java模擬rank()、over()函數(shù)獲取分組排名的方法設(shè)計(jì)及實(shí)現(xiàn),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-04-04

