SpringBoot自動配置的原理及實現
SpringBoot自動配置的實現原理
SpringBoot的核心就是自動配置,自動配置又是基於條件判斷來配置Bean。關於自動配置的原始碼在spring-boot-autoconfigure-2.0.3.RELEASE.jar
回顧配置屬性
在通常需要我們在property中配置資訊時,通常使用@ConfigurationProperties(pefix=“字首”)註解的方式從配置檔案中獲取配置,如下:
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.boot.context.properties.ConfigurationProperties; @RestController @ConfigurationProperties(prefix = "test") //@Component //如果這裡添加了註解那麼在自動配置類的時候就不用新增@enableConfigurationProperties(HelloProperties.class)註解. public class Demo { private String msg="default";//現在我們在配置檔案寫hello.msg=world,因為簡單就不再展示;如果那麼預設為default. public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } @RequestMapping("/msg") public Object index(){ return this.msg; } }
application.yml中配置資訊
test:
msg: bamboo
如果把application.yml中的配置資訊註釋掉則預設使用default值,否則使用配置資訊中的值,以上便是普通配置方式
解析
SpringBoot執行原理
先看@SpringBootApplication
@SpringBootConfiguration:標記當前類為配置類
@EnableAutoConfiguration:開啟自動配置
@ComponentScan:掃描主類所在的同級包以及下級包裡的Bean
關鍵是@EnableAutoConfiguration
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
而通過@Import(AutoConfigurationImportSelector.class)匯入的配置功能,
AutoConfigurationImportSelector中的方法getCandidateConfigurations,得到待配置的class的類名集合,這個集合就是所有需要進行自動配置的類,而是是否配置的關鍵在於META-INF/spring.factories檔案中是否存在該配置資訊
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; }
開啟,如下圖可以看到所有需要配置的類全路徑都在檔案中,每行一個配置,多個類名逗號分隔,而\表示忽略換行
以SpringApplicationAdminJmxAutoConfiguration類來看其主要構成部分
@Configuration
@AutoConfigureAfter({JmxAutoConfiguration.class}) //配置完JmxAutoConfiguration後再配置當前型別
// spring.application.admin為字首,屬性為enabled,有值時為true,沒有匹配到則為false:以上條件為true則例項化,否則不是例項化
@ConditionalOnProperty( prefix = "spring.application.admin", value = {"enabled"}, havingValue = "true", matchIfMissing = false)
public class SpringApplicationAdminJmxAutoConfiguration
都能看到各種各樣的條件判斷註解,滿足條件時就載入這個Bean並例項化
此類的條件註解是:@ConditionalOnProperty
@ConditionalOnBean:當容器裡有指定Bean的條件下
@ConditionalOnClass:當類路徑下有指定的類的條件下
@ConditionalOnExpression:基於SpEL表示式為true的時候作為判斷條件才去例項化
@ConditionalOnJava:基於JVM版本作為判斷條件
@ConditionalOnJndi:在JNDI存在的條件下查詢指定的位置
@ConditionalOnMissingBean:當容器裡沒有指定Bean的情況下
@ConditionalOnMissingClass:當容器裡沒有指定類的情況下
@ConditionalOnWebApplication:當前專案時Web專案的條件下
@ConditionalOnNotWebApplication:當前專案不是Web專案的條件下
@ConditionalOnProperty:指定的屬性是否有指定的值
@ConditionalOnResource:類路徑是否有指定的值
@ConditionalOnOnSingleCandidate:當指定Bean在容器中只有一個,或者有多個但是指定首選的Bean
這些註解都組合了@Conditional註解,只是使用了不同的條件組合最後為true時才會去例項化需要例項化的類,否則忽略
這種spring4.X帶來的動態組合很容易後期配置,從而避免了硬編碼,使配置資訊更加靈活多變,同時也避免了不必要的意外異常報錯。使用的人只要知道配置的條件即可也不用去閱讀原始碼,方便快捷,這也是sprignboot快捷方式帶來的好處
參考HttpEncodingAutoConfiguration配置資訊如下
@Configuration
@EnableConfigurationProperties(HttpEncodingProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
@Configuration:標明為配置類
@EnableConfigurationProperties(HttpEncodingProperties.class)宣告開啟屬性注入
@ConditionalOnClass(CharacterEncodingFilter.class)當CharacterEncodingFilter在類路徑的條件下
@ConditionalOnProperty(prefix = “spring.http.encoding”, value = “enabled”, matchIfMissing = true)當spring.http.encoding=enabled的情況下,如果沒有設定則預設為true,即條件符合
@ConditionalOnMissingBean當容器中沒有這個Bean時新建Bean
案例擴充套件
/**
* @author wuweifeng wrote on 2017/11/25.
* 根據部署環境動態決定是否啟用eureka
線上的環境開啟eureka,就在application-prod.yml裡配上open.eureka=true,
其他的yml什麼也不寫就行了。這樣本地啟動時就相當於沒有開啟EnableDiscoveryClient
*/
@Component
@ConditionalOnProperty(value = "open.eureka")
@EnableDiscoveryClient
public class JudgeEnableDiscoveryClient
自己實現一個自己的自動配置
專案
xm-common:普通jar專案
- src/main
java
BambooServer.java 需要被例項化的服務類
BambooServerProperties.java 配置資訊屬性類
BmbooServiceAutoConfiguration.java 自動配置類
resources
META-INF/spring.factories 配置自動配置的屬性檔案
demo:普通springboot-web專案
需要例項化的服務類
public class BambooServer {
private String name;
public String sayServerName(){
return "I'm " + name + "! ";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
配置資訊對應的屬性對映類,需要pom中加入spring-boot-starter依賴
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "bamboo")
public class BambooServerProperties {
private static final String NAME = "bamboo_server0";
private String name = NAME;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
自動配置檔案
/**
* Author: bamboo
* Time: 2018/11/25/025
* Describe: 自動配置類
* 根據條件判斷是否要自動配置,建立Bean
*/
@Configuration
@EnableConfigurationProperties(BambooServerProperties.class)
@ConditionalOnClass(BambooServer.class)//判斷BambooServer這個類在類路徑中是否存在
@ConditionalOnProperty(prefix = "bamboo",value = "enabled",matchIfMissing = true)
public class BmbooServiceAutoConfiguration {
@Autowired
private BambooServerProperties mistraServiceProperties;
@Bean(name = "bambooServer")
@ConditionalOnMissingBean(BambooServer.class)//當容器中沒有這個Bean時(BambooServer)就自動配置這個Bean,Bean的引數來自於BambooServerProperties
public BambooServer mistraService(){
BambooServer mistraService = new BambooServer();
mistraService.setName(mistraServiceProperties.getName());
return mistraService;
}
}
在建立如下路徑檔案src/main/resources/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bamboo.common.autoconfigure.bamboo.BmbooServiceAutoConfiguration
必須是自動配置類的全路徑
mvn install 該專案
建立一個springboot-mvc專案pom依賴上面的jar
@SpringBootApplication
@RestController
@Import(value = {CorsConfig.class, LogFilter.class}) //跨域,介面訪問請求日誌
public class DemoApplication {
@Autowired
private BambooServer bmbooService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@RequestMapping("/")
public Object index(){
return "helll demo"+bmbooService.getName()+DateUtils.getDate();
}
}
在applicaton.yml中加,重啟重新整理則會更新為如下資訊
bamboo:
name: 測試服務