亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Springboot中的@Conditional注解詳解

 更新時(shí)間:2023年09月25日 08:45:29   作者:怪 咖@  
這篇文章主要介紹了Springboot中的@Conditional注解詳解,@Conditional來源于spring-context包下的一個(gè)注解,Conditional中文是條件的意思,@Conditional注解它的作用是按照一定的條件進(jìn)行判斷,滿足條件給容器注冊bean,需要的朋友可以參考下

一、@Conditional源碼

@Conditional 來源于 spring-context 包下的一個(gè)注解。Conditional中文是條件的意思,@Conditional注解它的作用是按照一定的條件進(jìn)行判斷,滿足條件給容器注冊bean。

通過他的注解內(nèi)部可以發(fā)現(xiàn),他就是一個(gè)純功能性注解,他并沒有依賴于其他注解,類上只有三個(gè)元注解。

  • @Target({ElementType.TYPE, ElementType.METHOD}) 使用范圍接口、類、枚舉、注解、方法
  • @Retention(RetentionPolicy.RUNTIME): @Retention是用來修飾注解的生命周期的,RetentionPolicy.RUNTIME代表的是不僅被保存到class文件中,jvm加載class文件之后,仍然存在;一直有效!
  • @Documented: @Documented和@Deprecated注解長得有點(diǎn)像,@Deprecated是用來標(biāo)注某個(gè)類或者方法不建議再繼續(xù)使用,@Documented只能用在注解上,如果一個(gè)注解@B,被@Documented標(biāo)注,那么被@B修飾的類,生成Javadoc文檔時(shí),會(huì)顯示@B。
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

@Conditional 只有一個(gè)參數(shù),并且這個(gè)參數(shù)要求是繼承與 Condition 類,并且參數(shù)是個(gè)數(shù)組,也就是可以 傳多個(gè)的。 Condition 類是一個(gè)函數(shù)式接口(只有一個(gè)方法的接口被稱為函數(shù)式接口)。 matches 方法就是比較方法,如果為 true 則注入,如果為 false 則不注入。

@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

二、@Conditional擴(kuò)展注解

而除了@Conditional注解外,springboot通過@Conditional注解又?jǐn)U展了很多注解出來,如下@ConditionalOnBean、@ConditionalOnClass等等…

在這里插入圖片描述

在這里插入圖片描述

三、@Conditional實(shí)戰(zhàn)

(1)自定義Condition實(shí)現(xiàn)類

public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String isOpen = environment.getProperty("systemLog.isOpen");
        // 就算沒有設(shè)置systemLog.isOpen,那么isOpen就等于null,Boolean.valueOf對于null照樣會(huì)返回false的
        return Boolean.valueOf(isOpen);
    }
}

(2)自定義一個(gè)實(shí)體類,用于測試

public class TestBean1 {
    @Override
    public String toString() {
        return super.toString() + "--我是TestBean1";
    }
}

(3)添加配置類,并在類上使用@Conditional

@Configuration
@Conditional(MyCondition.class)
public class Myconfig {
    @Bean
    public TestBean1 testBean1(){
        return new TestBean1();
    }
}

(4)添加測試類

@RestController
public class CommonController {
    @Autowired(required = false)
    private Myconfig myconfig;
	@Autowired(required = false)
    private TestBean1 testBean1;
    @RequestMapping("/import")
    public void printImportBeanInfo() {
        System.out.println(myconfig);
        System.out.println(testBean1);
    }
}

(5)啟動(dòng)測試: 訪問 //localhost:8080/import ,可見Myconfig類并沒有注入到容器,按正常來說被@Configuration修飾之后是會(huì)存放到容器當(dāng)中的,但是顯然因?yàn)锧Conditional判斷為false所以沒有注入到容器當(dāng)中。

在這里插入圖片描述

通過這個(gè)測試不難發(fā)現(xiàn)假如@Bean所在的類沒有注入到容器當(dāng)中,那么他也不會(huì)被注入到容器當(dāng)中。

(6)在 application.yml 當(dāng)中添加如下配置,然后再啟動(dòng)測試。

systemLog:
  isOpen: true

在這里插入圖片描述

(7)@Conditional還可以應(yīng)用于方法上,我們可以讓他和@Bean注解來配合使用

@Configuration
public class Myconfig {
    @Bean
    @Conditional(MyCondition.class)
    public TestBean1 testBean1(){
        return new TestBean1();
    }
}

四、@Conditional多條件

前言中說,@Conditional注解傳入的是一個(gè)Class數(shù)組,存在多種條件類的情況。

這種情況貌似判斷難度加深了,測試一波,新增新的條件類,實(shí)現(xiàn)的 matches 返回 false (這種寫死返回false的方法純屬測試用,沒有實(shí)際意義O(∩_∩)O)

public class ObstinateCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
         return false;
    }
}
@Configuration
@Conditional({MyCondition.class,ObstinateCondition.class})
public class Myconfig {
    @Bean
    public TestBean1 testBean1(){
        return new TestBean1();
    }
}

測試結(jié)果得知:

  • 第一個(gè)條件類實(shí)現(xiàn)的方法返回true,第二個(gè)返回false,則結(jié)果false,不注入進(jìn)容器。
  • 第一個(gè)條件類實(shí)現(xiàn)的方法返回true,第二個(gè)返回true,則結(jié)果true,注入進(jìn)容器中。

五、常見的擴(kuò)展注解

關(guān)于這些擴(kuò)展注解其實(shí)在官網(wǎng)源碼當(dāng)中是有注釋的,感興趣的可以看一下:

5.1.@ConditionalOnClass

主要是判斷是否存在這個(gè)類文件,如果有這個(gè)文件就相當(dāng)于滿足條件,然后可以注入到容器當(dāng)中。

當(dāng)然并不是說容器里面是否有這個(gè)類哈,不要理解錯(cuò)了,這也就是我們有時(shí)候使用springboot只需要引入個(gè)依賴,框架就可以用的原因!

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnClass {
	// 必須出現(xiàn)的類
    Class<?>[] value() default {};
	// 必須存在的類名,必須是全限類名,也就是包含包名+類名。
    String[] name() default {};
}

用法示例:

@Configuration
@ConditionalOnClass({TestBean2.class})
public class Myconfig {
    @Bean
    @ConditionalOnClass(name = "com.gzl.cn.springbootcache.config.TestBean3")
    public TestBean1 testBean1(){
        return new TestBean1();
    }
}

5.2.@ConditionalOnMissingClass

@ConditionalOnMissingClass 只有一個(gè) value 屬性。他和 @ConditionalOnClass 功能正好相反,@ConditionalOnClass是class存在為true,而@ConditionalOnMissingClass是不存在為true,也就是存在為false。為fasle就意味著不注入。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnMissingClass {
	// 必須不存在的類名稱,全類名
    String[] value() default {};
}
@ConditionalOnMissingClass("com.gzl.cn.springbootcache.config.TestBean5")

或者

@ConditionalOnMissingClass(value = "com.gzl.cn.springbootcache.config.TestBean5")

5.3.@ConditionalOnBean

bean存在的時(shí)候注入,不存在的時(shí)候不注入,這塊就是指的spring的ioc容器了。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnBean {
	// 指定bean的類類型。當(dāng)所有指定類的bean都包含在容器中時(shí),條件匹配。
    Class<?>[] value() default {};
	// 指定bean的全類名。當(dāng)指定的所有類的bean都包含在容器中時(shí),條件匹配。
    String[] type() default {};
	// bean所聲明的注解,當(dāng)ApplicationContext中存在聲明該注解的bean時(shí)返回true
    Class<? extends Annotation>[] annotation() default {};
	// bean的id,當(dāng)ApplicationContext中存在給定id的bean時(shí)返回true,這個(gè)id指的就是容器當(dāng)中對象的id
    String[] name() default {};
	// 搜索容器層級(jí),默認(rèn)是所有上下文搜索
    SearchStrategy search() default SearchStrategy.ALL;
	// 可能在其泛型參數(shù)中包含指定bean類型的其他類
    Class<?>[] parameterizedContainer() default {};
}

(1)代碼示例:假如我把testBean2方法刪掉,那么testBean1也將會(huì)不注入。

@Configuration
public class Myconfig {
    @Bean
    public TestBean2 testBean2(){
        return new TestBean2();
    }
    @Bean
    @ConditionalOnBean(TestBean2.class)
    public TestBean1 testBean1() {
        return new TestBean1();
    }
}

(2)測試類

@RestController
public class CommonController {
    @Autowired(required = false)
    private TestBean1 testBean1;
    @Autowired(required = false)
    private TestBean2 testBean2;
    @Autowired(required = false)
    private Myconfig myconfig;
    @RequestMapping("/import")
    public void printImportBeanInfo() {
        System.out.println(myconfig);
        System.out.println(testBean1);
        System.out.println(testBean2);
    }
}

(3)運(yùn)行輸出結(jié)果:會(huì)發(fā)現(xiàn)三個(gè)對象是都注入到容器當(dāng)中了。

(4)目前存在的問題

注意這里還存在一個(gè)執(zhí)行順序問題,假如我把以下代碼放到啟動(dòng)類當(dāng)中,而不是 Myconfig 配置文件當(dāng)中,這時(shí)候會(huì)發(fā)現(xiàn)一個(gè)問題,testBean2注入進(jìn)去了,但是帶有 @ConditionalOnBean(TestBean2.class) 條件的 testBean1 沒有注入進(jìn)去。原因其實(shí)很簡單,執(zhí)行 testBean1 的時(shí)候,testBean2并沒有注入進(jìn)去,然后testBean1就注入失敗了,緊接著失敗后testBean2又注入進(jìn)來了。

@Bean
public TestBean2 testBean2(){
    return new TestBean2();
}

(5)緊接著我又做了個(gè)試驗(yàn),把啟動(dòng)類當(dāng)中的testBean2刪掉了,又創(chuàng)建了如下一個(gè)配置類,但是testBean1還是注入失敗。

@Configuration
public class MyconfigTest {
    @Bean
    public TestBean2 testBean2(){
        return new TestBean2();
    }
}

(6)想要解決這個(gè)問題很簡單,想辦法讓 MyconfigTest 比 Myconfig 先加載就可以了

于是我在MyconfigTest 添加了如下order注解

@Order(Ordered.HIGHEST_PRECEDENCE) //最高優(yōu)先級(jí)

然后在Myconfig 也添加了Order注解

@Order(Ordered.LOWEST_PRECEDENCE) //最低優(yōu)先級(jí)

但是仍然沒有解決該問題。@Configuration并不能通過@Order指定順序。

(7)大膽猜測下: @Configuration通過配置類名的自然順序來加載的。

在這里插入圖片描述

將MyconfigTest改名字:還別說改完名字真的就可以了!

在這里插入圖片描述

(8)不可能每次遇到這種問題都改名字吧,經(jīng)查文檔,終于找到了需要的東西:我們可以通過@AutoConfigureBefore,@AutoConfigureAfter來控制配置類的加載順序。

在MyconfigTest類上添加 @AutoConfigureBefore(Myconfig.class) ,意思是在Myconfig實(shí)例化之前加載。如果要讓@AutoConfigureBefore生效,還需要在META-INF/spring.factories文件中添加如下內(nèi)容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.gzl.cn.springbootcache.config.MyconfigTest,\
com.gzl.cn.springbootcache.config.Myconfig

在這里插入圖片描述

最終成功解決哈!

5.4.@ConditionalOnMissingBean

bean不存在的時(shí)候注入,存在的時(shí)候?yàn)閒alse。跟@ConditionalOnBean正好是相反的。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnMissingBean {
    Class<?>[] value() default {};
    String[] type() default {};
	// 識(shí)別匹配 bean 時(shí),可以被忽略的 bean 的 class 類型
    Class<?>[] ignored() default {};
	//識(shí)別匹配 bean 時(shí),可以被忽略的 bean 的 class 類型名稱
    String[] ignoredType() default {};
    Class<? extends Annotation>[] annotation() default {};
    String[] name() default {};
    SearchStrategy search() default SearchStrategy.ALL;
    Class<?>[] parameterizedContainer() default {};
}

@ConditionalOnMissingBean 代表的是如果容器里面沒有 TestBean1 的實(shí)例,那么就運(yùn)行@Bean修飾的方法注入對象,不管注入的什么對象。

@Configuration
public class Myconfig {
    @Bean
    @ConditionalOnMissingBean(TestBean1.class)
    public TestBean1 testBean1() {
        return new TestBean1();
    }
}

注意: @ConditionalOnMissingBean 和 @ConditionalOnBean 使用的時(shí)候是可以不帶任何屬性的,不帶任何屬性的時(shí)候他就是判斷的當(dāng)前注入的類型。@ConditionalOnBean是判斷當(dāng)沒有的時(shí)候進(jìn)行注入,例如如下:他只會(huì)注入一個(gè)TestBean1進(jìn)去。

@Configuration
public class Myconfig {
    @Bean
    @ConditionalOnMissingBean
    public TestBean1 testBean1() {
        System.out.println("1111111111");
        return new TestBean1();
    }
    @Bean
    @ConditionalOnMissingBean
    public TestBean1 testBean2() {
        System.out.println("2_222_222_222");
        return new TestBean1();
    }
}

5.5.@ConditionalOnProperty

@ConditionalOnProperty 主要可用于通過和 springboot 當(dāng)中 application 配置文件來使用。在實(shí)戰(zhàn)當(dāng)中我們也可以通過他來實(shí)現(xiàn)配置化管理bean。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
	// 指定的屬性完整名稱,不能和name同時(shí)使用。
    String[] value() default {};
	// //配置文件中屬性的前綴
    String prefix() default "";
	// //指定的屬性名稱
    String[] name() default {};
	// 指定的屬性的屬性值要等于該指定值,當(dāng)value或name為一個(gè)時(shí)使用
    String havingValue() default "";
	// 當(dāng)不匹配時(shí)是否允許加載,當(dāng)為true時(shí)就算不匹配也不影響bean的注入或配置類的生效。
    boolean matchIfMissing() default false;
}

使用示例:

@Configuration
@ConditionalOnProperty(
        prefix = "system.log",
        name = {"open"},
        havingValue = "true",
        matchIfMissing = false
)
public class Myconfig {
    @Bean
    public TestBean1 testBean1() {
        return new TestBean1();
    }
}

然后只要在application添加如下配置,Myconfig和testBean1就會(huì)注入到容器當(dāng)中。如果不設(shè)置也不會(huì)報(bào)錯(cuò),只是注入不到容器里而已。

system:
  log:
    open: true

5.6.其他注解

  • @ConditionalOnJava:只有運(yùn)行指定版本的 Java 才會(huì)加載 Bean
  • @ConditionalOnWebApplication 和 @ConditionalOnNotWebApplication:只有運(yùn)行在(不在)web 應(yīng)用里才會(huì)加載這個(gè) bean
  • @ConditionalOnCloudPlatform:只有運(yùn)行在指定的云平臺(tái)上才加載指定的 bean
  • @ConditionalOnJndi:只有指定的資源通過 JNDI 加載后才加載 bean
  • @ConditionalOnExpression(“${test.express}==true”) :可通過spring提供的spEL表達(dá)式靈活配置,當(dāng)表達(dá)式為true的時(shí)候,才會(huì)實(shí)例化一個(gè)Bean
  • @ConditionalOnSingleCandidate(UserService.class) :表示ioc容器中只有一個(gè)UserService類型的Bean,才生效
  • @ConditionalOnResource:指定的靜態(tài)資源?件存在 才加載

六、總結(jié)

在這里插入圖片描述

到此這篇關(guān)于Springboot中的@Conditional注解詳解的文章就介紹到這了,更多相關(guān)@Conditional注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論