自動配置絕對算得上是Spring Boot的最大亮點,完美的展示了CoC約定優於配置; Spring Boot能自動配置Spring各種子項目(Spring MVC, Spring Security, Spring Data, Spring Cloud, Spring Integration, Spring Batch等)以及第三方開源框架所需要定義的各種Bean。 Spring Boot內部定義了各種各樣的XxxxAutoConfiguration配置類,預先定義好了各種所需的Bean。只有在特定的情況下這些配置類才會被起效。
一、概述
SpringBoot的Auto-configuration的核心實現都位於spring-boot-autoconfigure-xxx.jar;其中SpringBoot將依據classpath里面的依賴內容來自動配置bean到IoC容器,Auto-configuration會嘗試推斷哪些beans是用戶可能會需要的。
自動化配置並不是spring boot才有的,從spring framework3.1開始,這個特性就有了,像@EnableAspectJAutoProxy、@EnableAsync都是從spring 3.1開始就有了。org.springframework.context.annotation包下面擁有自動配置的所有的相關的基礎設施。
基礎設施
org.springframework.context.annotation包下面提供了各種基於注解配置的基礎設施:
1. @Profile:可跟@Bean配合、
2. @Bean、@Scope、@DependsOn、@Primary、@Lazy、@Role、@Description:
3. @Conditional、Condition:@Conditional注解標識在類或者方法上,標識在方法上,符合條件,創建該方法的返回值類型的Bean;標識在類上,符合條件全部創建。
4. @Import(@ImportResource):
5. @Configuration表示的Class(@EnableLoadTimeWeaving)、ImportSelector接口實現(@EnableAsync)或者ImportBeanDefinitionRegistrar接口實現(@EnableAspectJAutoProxy)
6. ImportSelector、DeferredImportSelector:
7. ImportRegistry
8. ImportBeanDefinitionRegistrar:用來手動注冊bean定義的, 可以實現類似於Mybatis-Spring提供的掃描Mapper接口並注冊其bean定義, 事實上@MapperScan注解就@Import了MapperScannerRegistrar這個類, 而這個類實現了上面的接口, 來掃描Mapper並注冊bean定義.再多說點吧, Spring解析Java配置類的時候, 會判斷類是不是標注了@Import注解, 然后會判斷, 如果Import注解的value是ImportBeanDefinitionRegistrar類型, 會存到一個變量, 后面初始化bean工程完成后, 會回調ImportBeanDefinitionRegistrar.
9. @Configuration:跟@Controller、@Servcice和@Repository是一樣的套路,都用@Component注解了,作為特定類型的組件
10. @PropertySource
11. Condition、ConfigurationCondition、@Conditional
spring boot autoconfigure
Spring Boot AutoConfigure替代了XML風格的配置文件,帶來了前所未有的體驗。Spring Boot AutoConfigure模塊基於Spring Framework和Spring Boot提供的基礎設施,構建類配置Bean+屬性文件配置行為的配置方式,Java類配置Bean為我們提供了更好的編程體驗,屬性文件配置行為的方式使這種方式擁有跟XML外部配置文件配置方式同樣的靈活性。
org.springframework.boot.autoconfigure
首先,Spring Boot AutoConfigure在Spring Framework和Spring Boot提供的基礎設施上做了很多的擴展工作:
1. 順序控制:AutoConfigureOrder、AutoConfigureAfter、AutoConfigureBefore;
2. AutoConfigurationPackage:在spring boot mian class上標識EnableAutoConfiguration之后,所有子包下面的spring 組件都能被掃描到,就是這個注解的能力;
3. EnableAutoConfiguration/ImportAutoConfiguration:EnableAutoConfiguration開啟自動配置,自動應用spring.factories中配置的各種*AutoConfiguration;ImportAutoConfiguration跟EnableAutoConfiguration相比,只是沒有自動配置的功能,給ImportAutoConfiguration傳入誰的AutoConfiguration就應用誰的,單元測試等的場景用到的比較多;
4. 其他的一些工具類,過濾器之類的東西大家可以自己去看下
org.springframework.boot.autoconfigure.context.condition
ConditionalOnCloudPlatform:是否在雲環境下,spring boot cloud模塊提供了兩種實現,CLOUD_FOUNDRY和HEROKU,國內應該用不到這個注解了
ConditionalOnJava:指定的Java版本
ConditionalOnWebApplication:是Web環境的時候
ConditionalOnNotWebApplication:不是web環境的時候
ConditionalOnJndi:JNDI環境下使用
ConditionalOnClass:classpath中存在某個類
ConditionalOnMissingClass:classpath中不存在某個類
ConditionalOnBean:BeanFactory中存在某個類的Bean
ConditionalOnMissingBean:BeanFactory中不存在某個類的Bean
ConditionalOnExpression:SpEL的結果
ConditionalOnProperty:Environment中是否有某個屬性的配置信息
ConditionalOnResource:classpath中是否存在指定名稱的資源
ConditionalOnSingleCandidate:指定的類在BeanFactory中只有一個候選的bean,或者有多個候選的bean,但是其中一個指定了primary時
各種*AutoConfiguration的實現:
所有的*AutoConfiguration的具體實現包括兩部分,一個是標識了@Configuration注解的配置類,另一個是Property文件。有些模塊比較復雜,像security的oauth2模塊,主要文件也是這兩類,剩下的是一些工具。
*AutoConfiguration也是Configuration,被@Configuration注解,只不過spring boot autoconfigure模塊內置的 *AutoConfiguration被配置到了 spring.factories文件中,啟動的時候自動配置。
自動配置是Spring Boot的最大亮點,完美的展示了CoC約定優於配置。
二、源碼解析
2.1、從SpringBoot啟動時的自動配置加載過程
查看源碼可以看看自動配置類是如何被引入的。
a) 應用入口
@SpringBootApplication public class SpringBootDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringBootDemoApplication.class, args); } }
b) 類注解 @SpringBootApplication = @EnableAutoConfiguration + @ComponentScan + @Configuration(而其中的@EnableAutoConfiguration
則正是實現Auto Config的關鍵之所在)
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { // ... } @Configuration public @interface SpringBootConfiguration { // ... }
c)開啟自動配置注解 @EnableAutoConfiguration,是auto config 關鍵所在。
@AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { // ... }
d)導入配置類 EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector
@EnableAutoConfiguration
注解會導入AutoConfigurationImportSelector
類的實例被引入到Spring容器中,而該類的繼承鏈如下:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
因此對於 AutoConfigurationImportSelector類, 我們重點關注的是其實現自ImportSelector接口的方法selectImports,而直接繼承的DeferredImportSelector作為一個標志性接口,主要作用是為了Defer(推遲;延期;)。
在我們繼續探索之前,讓我們暫停一下,先來回顧下Spring是如何執行到這里來的,即如何調用到AutoConfigurationImportSelector.selectImports方法的。
在selectImports方法上打個斷點,啟動任意一個springboot項目,調用鏈如下圖:
從上述堆棧中我們可以看到 ConfigurationClassParser.parse() 被調用,而其參數candidates ,作為一個集合參數其中只包含我們在啟動SpringBoot時傳入的那個AutoConfigSpringBootApplication類包裹所形成的BeanDefinitionHolder實例。
該ConfigurationClassParser.parse(Set<BeanDefinitionHolder> configCandidates)方法最終會調用到自身內部私有的processDeferredImportSelectors()方法:
// 本方法位於 protected 訪問級別的 ConfigurationClassParser 中 private void processDeferredImportSelectors() { // @EnableAutoConfiguration注解上修飾的@Import(AutoConfigurationImportSelector.class) 注解的解析是由 ConfigurationClassParser.parse中開始調度完成(本類中的processImports方法), 進而載入到本類的 deferredImportSelectors 字段中。 // 這里要特別注意,正因為AutoConfigurationImportSelector是一個DeferredImportSelector實例,所以其生效時機晚於@Import生效的時機,這也使得邏輯時序可以正確地運行下去。 List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR); for (DeferredImportSelectorHolder deferredImport : deferredImports) { // 這里取到的configClass就是我們自定義的 AutoConfigSpringBootApplication ConfigurationClass configClass = deferredImport.getConfigurationClass(); try { // 核心邏輯就是下面這兩句了 // 首先是這行, 負責回調我們上面使用@Import導入的AutoConfigurationImportSelector里的邏輯實現, 詳情將在本文接下來的內容 // 最終的返回值是經過篩選,滿足要求的類名 String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata()); // configClass 就是我們傳遞給 SpringApplication.run 的AutoConfigSpringBootApplication類 // 該方法最終會跳轉到 本類內部的doProcessConfigurationClass方法中,來將相應Bean注冊進容器, Auto Config完成。 processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false); } catch { // 異常處理略 } } }
繞了一圈終於回到本小節原本關注的內容——有關AutoConfigurationImportSelector
實現的selectImports
方法:
// AutoConfigurationImportSelector (位於package - org.springframework.boot.autoconfigure, 所以是SpringBoot自帶的) @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } try { // 加載 META-INF/spring-autoconfigure-metadata.properties 中的相關配置信息, 注意這主要是供Spring內部使用的 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); // 獲取所有通過META-INF/spring.factories配置的, 此時還不會進行過濾和篩選 // KEY為 : org.springframework.boot.autoconfigure.EnableAutoConfiguration 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); } }
A、getCandidateConfigurations()方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 獲取所有通過META-INF/spring.factories配置的, 此時還不會進行過濾和篩選KEY為:org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置的value(類路徑+類名稱) 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; } //返回EnableAutoConfiguration.class protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { //此時為org.springframework.boot.autoconfigure.EnableAutoConfiguration String factoryClassName = factoryClass.getName(); try { //配置項的默認位置META-INF/spring.factories 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(); //從多個配置文件中查找,例如我的有:spring-boot-admin-starter-client-1.5.6.jar!/META-INF/spring.factories和stat-log-0.0.1-SNAPSHOT.jar!/META-INF/spring.factories 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); } }
看上面的源碼可知通過SpringFactoriesLoader.loadFactoryNames()把多個jar的/META-INF/spring.factories配置文件中的有EnableAutoConfiguration配置項都抓出來。
spring.factories的位置截圖如下:
B、在完成了對spring.factories中所有的EnableAutoConfiguration的解析后,對其過濾,去重,排序等操作等,返回。
C、ConfigurationClassParser.processImports(),ConfigurationClassParser工具類自身的邏輯並不注冊bean定義,它的主要任務是發現@Configuration注解的所有配置類並將這些配置類交給調用者(調用者會通過其他方式注冊其中的bean定義),而對於非@Configuration注解的其他bean定義,比如@Component注解的bean定義,該工具類使用另外一個工具ComponentScanAnnotationParser掃描和注冊它們。
以上正是Springboot完成Auto Config功能的關鍵點之一了。在本實現中,SpringBoot只是告知Spring需要去加載(Import)哪些Config類,剩下的工作依然是Spring那已經經過千錘百煉的邏輯來完成; 這正是 “微核 + 擴展”的優秀架構設計經驗的極致體現。
2.2、springboot自動配置DIY
通過上面的源碼分析,可以將如下的spring.factories的所有配置類如下:
spring-boot-1.5.10.RELEASE.jar/META-INF/spring.factories
# Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
spring-boot-autoconfigure-1.5.10.RELEASE.jar/META-INF/spring.factories
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
相應在做war時也是把當前的SpringBootDemoApplication作為source傳給了ServletInitializer。
那么,我們可以這樣啟動springboot,注解類不用SpringApplication,配置類也可自行導入
2.3、關鍵類EnableAutoConfiguration
2.3.1、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,@EnableAutoConfiguration可以幫助SpringBoot應用將所有符合條件的@Configuration配置都加載到當前SpringBoot創建並使用的IoC容器。(Import主要是配合Configuration來使用的,用來導出更多的Configuration類,ConfigurationClassPostProcessor會讀取Import的內容來實現具體的邏輯。)借助於Spring框架原有的一個工具類:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自動配置(掃描每個jar中的spring.factories)!
- 再看EnableAutoConfiguration的方法,就兩個方法exclude和excludeName,作用是自動配置過程中包含和排查指定的類。
2.3.2、自定義EnableAutoConfiguration示例
1、自定義EnableAutoConfiguration,這里Import了MyEnableAutoConfigurationImport。
package com.dxz.autoconfig; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.autoconfigure.AutoConfigurationPackage; import org.springframework.context.annotation.Import; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(MyEnableAutoConfigurationImport.class) public @interface MyEnableAutoConfiguration { }
2、自定義EnableAutoConfigurationImport,注入了ClassLoader,並調用SpringFactoriesLoader.loadFactoryNames()方法,導出Configuration的類。
package com.dxz.autoconfig; import java.util.List; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.DeferredImportSelector; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.type.AnnotationMetadata; public class MyEnableAutoConfigurationImport implements DeferredImportSelector, BeanClassLoaderAware { private ClassLoader classLoader; public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public String[] selectImports(AnnotationMetadata importingClassMetadata) { List<String> beanNames = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader); return beanNames.toArray(new String[beanNames.size()]); } }
3、入口類,這里使用了MyEnableAutoConfiguration注解。
package com.dxz.autoconfig; import java.util.HashMap; import java.util.Map; import org.springframework.boot.SpringApplication; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Configuration @MyEnableAutoConfiguration public class CustomizeEnableAutoConfigure { public static void main(String[] args) { SpringApplication application = new SpringApplication(CustomizeEnableAutoConfigure.class); application.run(args); } @Controller public static class MyController { @RequestMapping @ResponseBody public Map index() { Map<String, String> map = new HashMap<String, String>(); map.put("hello", "world2"); return map; } } }
結果:
2.4、condition包
2.4.1、condition示例講解
package org.springframework.boot.autoconfigure.condition。
提供這些condition主要目的:上面我們討論的AutoConfigurationImportSelector只能告訴Spring哪些類需要加載,但判斷所配置的類是否可以被加載(即Auto Config里的Auto)是一個非常繁瑣的邏輯,如果由某個中央控制系統來處理的話,必然會造成代碼耦合和復雜性猛增,因此SpringBoot最終使用了一貫的做法——將判斷是否加載的權限下放給了各個需要進行自動配置的需求方本身,這樣在springboot中擴展了很多condition。
基於Spring的@Conditional,SpringBoot提供了豐富的條件配置
@ConditionalOnClass : classpath中存在該類時起效
@ConditionalOnMissingClass : classpath中不存在該類時起效
@ConditionalOnBean : DI容器中存在該類型Bean時起效
@ConditionalOnMissingBean : DI容器中不存在該類型Bean時起效
@ConditionalOnSingleCandidate : DI容器中該類型Bean只有一個或@Primary的只有一個時起效
@ConditionalOnExpression : SpEL表達式結果為true時
@ConditionalOnProperty : 參數設置或者值一致時起效
@ConditionalOnResource : 指定的文件存在時起效
@ConditionalOnJndi : 指定的JNDI存在時起效
@ConditionalOnJava : 指定的Java版本存在時起效
@ConditionalOnWebApplication : Web應用環境下起效
@ConditionalOnNotWebApplication : 非Web應用環境下起效
@AutoConfigureAfter:在指定的配置類初始化后再加載
@AutoConfigureBefore:在指定的配置類初始化前加載
@AutoConfigureOrder:數越小越先初始化
2.4.2、condition示例講解
1)@ConditionalOnBean
/@ConditionalOnMissingBean
當容器中存在/不存在某個bean時,加上此注解的bean被自動注入
package org.springframework.boot.autoconfigure.condition; @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnBeanCondition.class) public @interface ConditionalOnBean { Class<?>[] value() default {}; String[] type() default {}; Class<? extends Annotation>[] annotation() default {}; String[] name() default {}; SearchStrategy search() default SearchStrategy.ALL; }
2)@ConditionalOnJava
根據當前使用的JDK版本,判斷是否自動注入
//使用jdk8才注入此bean @Bean @ConditionalOnJava(ConditionalOnJava.JavaVersion.EIGHT) public Runnable runnableBean2() { return () ->{}; }
3)@
ConditionalOnProperty來控制Configuration是否生效
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { String[] value() default {}; //數組,獲取對應property名稱的值,與name不可同時使用 String prefix() default "";//property名稱的前綴,可有可無 String[] name() default {};//數組,property完整名稱或部分名稱(可與prefix組合使用,組成完整的property名稱),與value不可同時使用 String havingValue() default "";//可與name組合使用,比較獲取到的屬性值與havingValue給定的值是否相同,相同才加載配置 boolean matchIfMissing() default false;//缺少該property時是否可以加載。如果為true,沒有該property也會正常加載;反之報錯 boolean relaxedNames() default true;//是否可以松散匹配,至今不知道怎么使用的 }
如果該值為空,則返回false;
如果值不為空,則將該值與havingValue指定的值進行比較,如果一樣則返回true;否則返回false。
如果返回值為false,則該configuration不生效;為true則生效。
@Configuration //在application.properties配置"mf.assert",對應的值為true @ConditionalOnProperty(prefix="mf",name = "assert", havingValue = "true") public class AssertConfig { @Autowired private HelloServiceProperties helloServiceProperties; @Bean public HelloService helloService(){ HelloService helloService = new HelloService(); helloService.setMsg(helloServiceProperties.getMsg()); return helloService; } }
2.4.2.3、示例:(參考網絡)
2.4.2.3.1)、通過SpringBoot自帶的注解ConditionalOnProperty實現
#bean實例化條件配置項
conditionKey: 1.0
ManageImpl1.java代碼如下:(MyManage接口、ManageImpl2省略)
package com.dxz.palmpay.condition; import javax.annotation.PostConstruct; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; //僅當conditionKey==1.0的時候實例化這個類 @ConditionalOnProperty(name = "conditionKey", havingValue = "1.0") @Component public class ManageImpl1 implements MyManage { @Override public void sayHello() { System.out.println("我是實現類01"); } //為了效果,創建后打印一些信息 @PostConstruct public void init() { this.sayHello(); } }


2.4.2.3.2)、自定義條件注解
(1)思路
- name:String類型,用來接受application.properties的配置項的key
- havingValue:String數組類型,用來和name對應key的Value進行匹配
(2)定義注解
package com.dxz.palmpay.condition; import org.springframework.context.annotation.Conditional; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(CustomOnPropertyCondition.class) public @interface CustomConditionalOnProperty { String name() default ""; //havingValue數組,支持or匹配 String[] havingValue() default {}; }
(3)定義注解的匹配規則
package com.dxz.palmpay.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Map; /** * 自定義條件注解的驗證規則 */ public class CustomOnPropertyCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { Map<String, Object> annotationAttributes = annotatedTypeMetadata .getAnnotationAttributes(CustomConditionalOnProperty.class.getName()); String propertyName = (String) annotationAttributes.get("name"); String[] values = (String[]) annotationAttributes.get("havingValue"); if (0 == values.length) { return false; } String propertyValue = conditionContext.getEnvironment().getProperty(propertyName); // 有一個匹配上就ok for (String havingValue : values) { if (propertyValue.equalsIgnoreCase(havingValue)) { return true; } } return false; } }
@Component @CustomConditionalOnProperty(name = "db.version", havingValue = {"3"}) public class ManageImpl3 implements MyManage { @Component @CustomConditionalOnProperty(name = "db.version", havingValue = {"1","2","4"}) public class ManageImpl4 implements MyManage {
自定義Condition注解,主要就2步
(1)定義一個條件注解(2)定義一個條件的校驗規則
參考:https://blog.csdn.net/lqzkcx3/article/details/82807888
參考:https://www.cnblogs.com/zeng1994/p/8c10310d8a042d56eddd40635afb6e93.html