Spring Boot中的那些條件判斷的實(shí)現(xiàn)方法
Spring Boot中的那些Conditional
spring boot中為我們提供了豐富的Conditional來讓我們得以非常方便的在項(xiàng)目中向容器中添加Bean。本文主要是對(duì)各個(gè)注解進(jìn)行解釋并輔以代碼說明其用途。
所有ConditionalOnXXX的注解都可以放置在class或是method上,如果方式在class上,則會(huì)決定該class中所有的@Bean注解方法是否執(zhí)行。
@Conditional
下面其他的Conditional注解均是語法糖,可以通過下面的方法自定義ConditionalOnXXX
Conditional注解定義如下,接收實(shí)現(xiàn)Condition接口的class數(shù)組。
public @interface Conditional {
Class<? extends Condition>[] value();
}
而Condition接口只有一個(gè)matchs方法,返回是否匹配的結(jié)果。
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
通過操作系統(tǒng)進(jìn)行條件判斷,從而進(jìn)行Bean配置。當(dāng)Window時(shí),實(shí)例化Bill的Person對(duì)象,當(dāng)Linux時(shí),實(shí)例化Linus的Person對(duì)象。
//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
這兩個(gè)注解會(huì)對(duì)Bean容器中的Bean對(duì)象進(jìn)行判斷,使用的例子是配置的時(shí)候,如果發(fā)現(xiàn)如果沒有Computer實(shí)例,則實(shí)例化一個(gè)備用電腦。
@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,如果注釋掉第一個(gè)@Bean,會(huì)實(shí)例化備用電腦,否則就不會(huì)實(shí)例化備用電腦
@ConditionalOnClass & @ConditionalOnMissingClass
這個(gè)注解會(huì)判斷類路徑上是否有指定的類,一開始看到的時(shí)候比較困惑,類路徑上如果沒有指定的class,那編譯也通過不了啊...這個(gè)主要用于集成相同功能的第三方組件時(shí)用,只要類路徑上有該組件的類,就進(jìn)行自動(dòng)配置,比如spring boot web在自動(dòng)配置視圖組件時(shí),是用Velocity,還是Thymeleaf,或是freemaker時(shí),使用的就是這種方式。
例子是兩套盔甲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是武士,使用套裝進(jìn)行戰(zhàn)斗
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Van {
private Fighter fighter;
public void fight(){
fighter.fight();
}
}
VanConfigA/B實(shí)例化武士
@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());
}
}
測(cè)試類,默認(rèn)情況,如果套裝AB都在類路徑上,兩套都會(huì)加載,A會(huì)設(shè)置為PRIMARY,如果在target class中將FightA.class刪除,則只會(huì)加載套裝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();
}
}
另外,嘗試將兩個(gè)VanConfigA/B合并,將注解ConditionalOnClass放到方法上,如果刪除一個(gè)套裝就會(huì)運(yùn)行出錯(cuò)。
@ConditionalOnExpress
依據(jù)表達(dá)式進(jìn)行條件判斷,這個(gè)作用和@ConditionalOnProperty大部分情況可以通用,表達(dá)式更靈活一點(diǎn),因?yàn)榭梢允褂肧pEL。例子中會(huì)判斷properties中test.enabled的值進(jìn)行判斷。BeanConfig分別對(duì)布爾,字符串和數(shù)字三種類型進(jìn)行判斷。數(shù)字嘗試了很多其他的方式均不行,比如直接使用==,貌似配置的屬性都會(huì)當(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
適合對(duì)單個(gè)Property進(jìn)行條件判斷,而上面的@ConditionalOnExpress適合面對(duì)較為復(fù)雜的情況,比如多個(gè)property的關(guān)聯(lián)比較。這個(gè)例子也給了三種基本類型的條件判斷,不過貌似均當(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的版本進(jìn)行判斷。
@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
通過指定的資源文件是否存在進(jìn)行條件判斷,比如判斷ehcache.properties來決定是否自動(dòng)裝配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
這個(gè)還沒有想到應(yīng)用場(chǎng)景,條件通過的條件是:1 對(duì)應(yīng)的bean容器中只有一個(gè) 2.對(duì)應(yīng)的bean有多個(gè),但是已經(jīng)制定了PRIMARY。例子中,BeanB裝配的時(shí)候需要看BeanA的裝配情況,所以BeanBConfig要排在BeanAConfig之后.可以修改BeanAConfig,將@Primary注解去掉,或者把三個(gè)@Bean注解去掉,BeanB就不會(huì)實(shí)例化了。
@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)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 詳解SpringBoot AOP 攔截器(Aspect注解方式)
- SpringBoot 注解事務(wù)聲明式事務(wù)的方式
- spring-boot通過@Scheduled配置定時(shí)任務(wù)及定時(shí)任務(wù)@Scheduled注解的方法
- 詳解Spring Boot集成MyBatis(注解方式)
- Spring boot中PropertySource注解的使用方法詳解
- SpringBoot2.0整合SpringCloud Finchley @hystrixcommand注解找不到解決方案
- Spring Boot 整合 Mybatis Annotation 注解的完整 Web 案例
- SpringBoot中自定義注解實(shí)現(xiàn)控制器訪問次數(shù)限制實(shí)例
- Spring Boot使用Value注解給靜態(tài)變量賦值的方法
相關(guān)文章
IDEA編譯時(shí)報(bào)常量字符串過長的解決辦法
本文主要介紹了IDEA編譯時(shí)報(bào)常量字符串過長的解決辦法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
Spring Boot自定義Banner實(shí)現(xiàn)代碼
這篇文章主要介紹了Spring Boot自定義Banner實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
MyBatis limit分頁設(shè)置的實(shí)現(xiàn)
這篇文章主要介紹了MyBatis limit分頁設(shè)置的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Spring事務(wù)失效的各種場(chǎng)景(13種)
本文主要介紹了Spring事務(wù)失效的各種場(chǎng)景,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

