Spring Boot中的那些條件判斷的實現(xiàn)方法
Spring Boot中的那些Conditional
spring boot中為我們提供了豐富的Conditional來讓我們得以非常方便的在項目中向容器中添加Bean。本文主要是對各個注解進行解釋并輔以代碼說明其用途。
所有ConditionalOnXXX的注解都可以放置在class或是method上,如果方式在class上,則會決定該class中所有的@Bean注解方法是否執(zhí)行。
@Conditional
下面其他的Conditional注解均是語法糖,可以通過下面的方法自定義ConditionalOnXXX
Conditional注解定義如下,接收實現(xiàn)Condition接口的class數(shù)組。
public @interface Conditional { Class<? extends Condition>[] value(); }
而Condition接口只有一個matchs方法,返回是否匹配的結(jié)果。
public interface Condition { boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
通過操作系統(tǒng)進行條件判斷,從而進行Bean配置。當(dāng)Window時,實例化Bill的Person對象,當(dāng)Linux時,實例化Linus的Person對象。
//LinuxCondition,為方便起見,去掉判斷代碼,直接返回true了 public class LinuxCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { return true; } }
//WindowsCondition,為方便起見,去掉判斷代碼,直接返回false了 public class WindowsCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) { return false; } } @Data
@ToString @AllArgsConstructor @NoArgsConstructor public class Person { private String name; private Integer age; }
//配置類 @Configuration public class BeanConfig { @Bean(name = "bill") @Conditional({WindowsCondition.class}) public Person person1(){ return new Person("Bill Gates",62); } @Bean("linus") @Conditional({LinuxCondition.class}) public Person person2(){ return new Person("Linus",48); } }
public class AppTest { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class); @Test public void test(){ String osName = applicationContext.getEnvironment().getProperty("os.name"); System.out.println("當(dāng)前系統(tǒng)為:" + osName); Map<String, Person> map = applicationContext.getBeansOfType(Person.class); System.out.println(map); } }
輸出的結(jié)果:
當(dāng)前系統(tǒng)為:Mac OS X
{linus=Person(name=Linus, age=48)}
@ConditionalOnBean & @ConditionalOnMissingBean
這兩個注解會對Bean容器中的Bean對象進行判斷,使用的例子是配置的時候,如果發(fā)現(xiàn)如果沒有Computer實例,則實例化一個備用電腦。
@Data @AllArgsConstructor @ToString public class Computer { private String name; }
@Configuration public class BeanConfig { @Bean(name = "notebookPC") public Computer computer1(){ return new Computer("筆記本電腦"); } @ConditionalOnMissingBean(Computer.class) @Bean("reservePC") public Computer computer2(){ return new Computer("備用電腦"); } }
public class TestApp { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class); @Test public void test1(){ Map<String,Computer> map = applicationContext.getBeansOfType(Computer.class); System.out.println(map); } }
修改BeanConfig,如果注釋掉第一個@Bean,會實例化備用電腦,否則就不會實例化備用電腦
@ConditionalOnClass & @ConditionalOnMissingClass
這個注解會判斷類路徑上是否有指定的類,一開始看到的時候比較困惑,類路徑上如果沒有指定的class,那編譯也通過不了啊...這個主要用于集成相同功能的第三方組件時用,只要類路徑上有該組件的類,就進行自動配置,比如spring boot web在自動配置視圖組件時,是用Velocity,還是Thymeleaf,或是freemaker時,使用的就是這種方式。
例子是兩套盔甲A(光明套裝)和B(暗黑套裝),如果A不在則配置B。
public interface Fighter { void fight(); } public class FighterA implements Fighter { @Override public void fight() { System.out.println("使用光明套裝"); } } public class FighterB implements Fighter { @Override public void fight() { System.out.println("使用暗黑套裝"); } }
Van是武士,使用套裝進行戰(zhàn)斗
@Data @AllArgsConstructor @NoArgsConstructor public class Van { private Fighter fighter; public void fight(){ fighter.fight(); } }
VanConfigA/B實例化武士
@Configuration @ConditionalOnClass({FighterA.class}) public class VanConfigA { @Primary @Bean public Van vanA(){ return new Van(new FighterA()); } } @Configuration @ConditionalOnClass({FighterB.class}) public class VanConfigB { @Bean public Van vanB(){ return new Van(new FighterB()); } }
測試類,默認(rèn)情況,如果套裝AB都在類路徑上,兩套都會加載,A會設(shè)置為PRIMARY,如果在target class中將FightA.class刪除,則只會加載套裝B。
@SpringBootApplication public class TestApp implements CommandLineRunner { @Autowired private Van van; public static void main(String[] args) { SpringApplication.run(TestApp.class, args); } @Override public void run(String... args) throws Exception { //do something van.fight(); } }
另外,嘗試將兩個VanConfigA/B合并,將注解ConditionalOnClass放到方法上,如果刪除一個套裝就會運行出錯。
@ConditionalOnExpress
依據(jù)表達(dá)式進行條件判斷,這個作用和@ConditionalOnProperty大部分情況可以通用,表達(dá)式更靈活一點,因為可以使用SpEL。例子中會判斷properties中test.enabled的值進行判斷。BeanConfig分別對布爾,字符串和數(shù)字三種類型進行判斷。數(shù)字嘗試了很多其他的方式均不行,比如直接使用==,貌似配置的屬性都會當(dāng)成字符串來處理。
@Data public class TestBean { private String name; }
@Configuration @ConditionalOnExpression("#{${test.enabled:true} }") //@ConditionalOnExpression("'zz'.equalsIgnoreCase('${test.name2}')") //@ConditionalOnExpression("new Integer('${test.account}')==1") public class BeanConfig { @Bean public TestBean testBean(){ return new TestBean("我是美猴王"); } }
@SpringBootApplication public class TestAppCommand implements CommandLineRunner { @Autowired private TestBean testBean; public static void main(String[] args) { SpringApplication.run(TestAppCommand.class, args); } @Override public void run(String... args) throws Exception { System.out.println(testBean.getName()); } }
@ConditionalOnProperty
適合對單個Property進行條件判斷,而上面的@ConditionalOnExpress適合面對較為復(fù)雜的情況,比如多個property的關(guān)聯(lián)比較。這個例子也給了三種基本類型的條件判斷,不過貌似均當(dāng)成字符串就可以...
@Data @AllArgsConstructor @NoArgsConstructor public class TestBean { private String name; }
@Configuration @ConditionalOnProperty(prefix = "test", name="enabled", havingValue = "true",matchIfMissing = false) //@ConditionalOnProperty(prefix = "test", name="account", havingValue = "1",matchIfMissing = false) //@ConditionalOnProperty(prefix = "test", name="name1", havingValue = "zz",matchIfMissing = false) public class BeanConfig { @Bean public TestBean testBean(){ return new TestBean("我是美猴王"); } }
@SpringBootApplication public class TestAppCommand implements CommandLineRunner { @Autowired private TestBean testBean; public static void main(String[] args) { SpringApplication.run(TestAppCommand.class, args); } @Override public void run(String... args) throws Exception { System.out.println(testBean.getName()); } }
@ConditionalOnJava
可以通過java的版本進行判斷。
@Data public class TestBean { }
@Configuration @ConditionalOnJava(JavaVersion.EIGHT) public class BeanConfig { @Bean public TestBean testBean(){ return new TestBean(); } }
public class TestApp { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); @Test public void test(){ Map<String,TestBean> map = context.getBeansOfType(TestBean.class); System.out.println(map); } }
@ConditionalOnResource
通過指定的資源文件是否存在進行條件判斷,比如判斷ehcache.properties來決定是否自動裝配ehcache組件。
@Data public class TestBean { }
@Configuration @ConditionalOnResource(resources = "classpath:application.yml") public class BeanConfig { @Bean public TestBean testBean(){ return new TestBean(); } }
public class TestApp { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); @Test public void test(){ Map<String,TestBean> map = context.getBeansOfType(TestBean.class); System.out.println(map); } }
@ConditionalOnSingleCandidate
這個還沒有想到應(yīng)用場景,條件通過的條件是:1 對應(yīng)的bean容器中只有一個 2.對應(yīng)的bean有多個,但是已經(jīng)制定了PRIMARY。例子中,BeanB裝配的時候需要看BeanA的裝配情況,所以BeanBConfig要排在BeanAConfig之后.可以修改BeanAConfig,將@Primary注解去掉,或者把三個@Bean注解去掉,BeanB就不會實例化了。
@Data @AllArgsConstructor @NoArgsConstructor public class BeanA { private String name; }
@Configuration public class BeanAConfig { @Bean @Primary public BeanA bean1(){ return new BeanA("bean1"); } @Bean(autowireCandidate = false) public BeanA bean2(){ return new BeanA("bean2"); } //@Bean(autowireCandidate = false) public BeanA bean3(){ return new BeanA("bean3"); } }
@Data public class BeanB { }
@Configuration @AutoConfigureAfter(BeanAConfig.class) @ConditionalOnSingleCandidate(BeanA.class) public class BeanBConfig { @Bean public BeanB targetBean(){ return new BeanB(); } }
public class TestApp { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanAConfig.class, BeanBConfig.class); @Test public void test(){ Map<String,BeanA> map = context.getBeansOfType(BeanA.class); System.out.println(map); Map<String,BeanB> map2 = context.getBeansOfType(BeanB.class); System.out.println(map2); } }
@ConditionalOnNotWebApplication & @ConditionalOnWebApplication
判斷當(dāng)前環(huán)境是否是Web應(yīng)用。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 詳解SpringBoot AOP 攔截器(Aspect注解方式)
- SpringBoot 注解事務(wù)聲明式事務(wù)的方式
- spring-boot通過@Scheduled配置定時任務(wù)及定時任務(wù)@Scheduled注解的方法
- 詳解Spring Boot集成MyBatis(注解方式)
- Spring boot中PropertySource注解的使用方法詳解
- SpringBoot2.0整合SpringCloud Finchley @hystrixcommand注解找不到解決方案
- Spring Boot 整合 Mybatis Annotation 注解的完整 Web 案例
- SpringBoot中自定義注解實現(xiàn)控制器訪問次數(shù)限制實例
- Spring Boot使用Value注解給靜態(tài)變量賦值的方法
相關(guān)文章
mac下修改idea的jvm運行參數(shù)解決idea卡頓的情況
這篇文章主要介紹了mac下修改idea的jvm運行參數(shù)解決idea卡頓的情況,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12Java微信公眾平臺開發(fā)(2) 微信服務(wù)器post消息體的接收
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺開發(fā)第二步,微信服務(wù)器post消息體的接收,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04java遞歸實現(xiàn)復(fù)制一個文件夾下所有文件功能
這篇文章主要介紹了java遞歸實現(xiàn)復(fù)制一個文件夾下所有文件功能n次,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-08-08Java中的數(shù)組復(fù)制(clone與arraycopy)代碼詳解
這篇文章主要介紹了Java中的數(shù)組復(fù)制(clone與arraycopy)代碼詳解,本文并未全部介紹數(shù)組復(fù)制的幾種方式,僅對clone和copy的相關(guān)內(nèi)容進行了解析,具有一定參考價值,需要的朋友可以了解下。2017-11-11