欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring中的@Conditional注解實現(xiàn)分析

 更新時間:2023年12月29日 09:21:14   作者:it_lihongmin  
這篇文章主要介紹了Spring中的@Conditional注解實現(xiàn)分析,  @Conditional是Spring 4出現(xiàn)的注解,但是真正露出價值的是Spring Boot的擴展@ConditionalOnBean等,需要的朋友可以參考下

@Conditional注解實現(xiàn)分析

@Conditional是Spring 4出現(xiàn)的注解,但是真正露出價值的是Spring Boot的擴展@ConditionalOnBean等。但是任然使用的是Spring框架進行處理,并沒有做太多定制的東西,所以還是先看看@Conditional注解的實現(xiàn)。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
 
	/**
	 * All {@link Condition Conditions} that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	Class<? extends Condition>[] value();
 
}

先看看@Conditional注解的結(jié)構(gòu)比較簡單,只需要定義一個Condition子類即可,并且說明只有滿足了當(dāng)前Condition的matches方法時才會將當(dāng)前@Component注冊成Bean。那么再看看Condition接口和體系。

/**
 * @since 4.0
 * @see ConfigurationCondition
 * @see Conditional
 * @see ConditionContext
 */
@FunctionalInterface
public interface Condition {
 
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

當(dāng)前會傳入ConditionContext和AnnotatedTypeMetadata進行回調(diào),返回是否匹配,如果不匹配則不會注冊成Bean。但是這是在哪里進行回調(diào)的呢?

ConfigurationClassParser # processConfigurationClass (ConfigurationClassParser # doProcessConfigurationClass等)

ConditionEvaluator # shouldSkip

比較清楚了,又是在處理@Import、@ComponentScan、ImportSelector等的處理類ConfigurationClassParser執(zhí)行時機比較清楚了

再看看Condition的結(jié)構(gòu)體系:

大致有四類

1)、ProfileCondition,項目啟動后Profile信息存放在ApplicationContext的Environment中,能拿到兩者之一就可以判斷。

2)、ConfigurationCondition

public interface ConfigurationCondition extends Condition {
 
    // 定義了子類必須實現(xiàn),返回下面枚舉的一種
    ConfigurationPhase getConfigurationPhase();
 
    // 判斷階段
	enum ConfigurationPhase {
            
        // Conponent階段,即將@Component加入到BeanFactory
        PARSE_CONFIGURATION,
 
        // 只有通過getBean才能正在將Bean注冊到Ioc容器中。前提是要將BeanDefinition添加到 
        // BeanFactory中
        REGISTER_BEAN
    }
}

3)、ConditionEvalutionReport,Spring Boot報表相關(guān)

4)、SpringBootCondition,直接是Spring Boot中擴展的。下一篇博客,具體分析 @ConditionalOnBean等再具體分析。

幾個的角色比較清楚了,只要一個@Conditional注解,注解的屬性為@Condition或其子類。根據(jù)回調(diào)@Condition的matches方法,即可判斷是否將注冊成Bean。

先看看回調(diào)時機,都是在處理@Component、@ComponentSacn、ImportSelector等情況注冊Bean時。

由于情況比較復(fù)雜,可能@Component上有@ComponentScan,則會遞歸進行處理,總之都會先調(diào)用ConfigurationClassParser的ConditionEvaluator conditionEvaluator的shouldSkip方法判斷是否跳過。

比如:

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(),  
           ConfigurationPhase.PARSE_CONFIGURATION)) {
 
        return;
    }
 
    // 省略其余代碼
}

而conditionEvaluator在ConfigurationClassParser的構(gòu)造器中被初始化,如下:

this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);

先看看ConditionEvaluator的類結(jié)構(gòu)

class ConditionEvaluator {
 
    private final ConditionContextImpl context;
 
    public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry,
			@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
 
		this.context = new ConditionContextImpl(registry, environment, resourceLoader);
	}
    
    // 省略其他方法
 
    private static class ConditionContextImpl implements ConditionContext {
 
        private final BeanDefinitionRegistry registry;
 
        private final ConfigurableListableBeanFactory beanFactory;
 
        private final Environment environment;
 
        private final ResourceLoader resourceLoader;
 
        private final ClassLoader classLoader;
 
        public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry,
                                    @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
 
            this.registry = registry;
            this.beanFactory = deduceBeanFactory(registry);
            this.environment = (environment != null ? environment : deduceEnvironment(registry));
            this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
            this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory);
        }
    }
}

也就是說,在ConfigurationClassParser構(gòu)造器中初始化ConditionEvaluator時候,就初始化了內(nèi)部類ConditionContextImpl,并且傳入了BeanFactory(Bean是否存在可以通過工廠進行判斷)、Environment(環(huán)境配置、Profile等存放在其中)、ResourceLoader(Spring的Resource加載器)、ClassLoader(類加載器)等。

到這里就比較清楚了,回調(diào)Condition的matches接口時傳入這些組件的類ConditionContextImpl,要實現(xiàn)@ConditionalOnBean、@OnPropertyCondition、@Profile、@ConditionalOnClass等都比較簡單了。

在調(diào)用Condition的matches時,不僅傳入了ConditionContextImpl,還出入了AnnotatedTypeMetadata,這是當(dāng)前注解結(jié)合被Spring封裝的注解元信息。理解比較抽象,比如自動裝配EmbeddedTomcat時需要同時存在Servlet、Tomcat、upgradeProtocol類;并且沒有將ServletWebServerFactory注冊成Bean,此時Component才能真正生效,如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
    // 省略其他代碼
}

具體再看看ConditionEvaluator的shouldSkip方法的實現(xiàn):

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    // 注解信息不能為空, 并且必須有Conditional或者其子類
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
        return false;
    }
    // 如果判斷階段為空,進行類型判斷再遞歸調(diào)用該方法
    if (phase == null) {
        if (metadata instanceof AnnotationMetadata &&
                ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
            return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
        }
        return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
    }
 
    List<Condition> conditions = new ArrayList<>();
    // 獲取多有的Condition類型,如上面的EmbeddedTomcat同時需要多個條件成立
    for (String[] conditionClasses : getConditionClasses(metadata)) {
        for (String conditionClass : conditionClasses) {
            Condition condition = getCondition(conditionClass, this.context.getClassLoader());
            conditions.add(condition);
        }
    }
    // 條件排序(根據(jù)Spring的那三個排序方式定義)
    AnnotationAwareOrderComparator.sort(conditions);
 
    for (Condition condition : conditions) {
        ConfigurationPhase requiredPhase = null;
        if (condition instanceof ConfigurationCondition) {
            requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
        }
        // 判斷階段為空(非ConfigurationCondition的子類)、不需要判斷階段,則直接返回true
        // 否則才調(diào)用matches接口判斷
        if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
            return true;
        }
    }
    return false;
}

1)、注解信息不能為空, 并且必須有Conditional或者其子類

2)、階段為null,則根據(jù)情況設(shè)置階段后再遞歸調(diào)用該方法

3)、獲取所有的Condition列表

4)、進行排序

5)、遍歷Condition,是否在該階段進行判斷。需要則再調(diào)用該Condition的matches方法

總結(jié):添加了@Conditional或者@ConditionXXX注解,其value值會對應(yīng)一個Condition或者子類的Class。在處理@ComponentScan、ImportSelector等時會根據(jù)判斷階段,調(diào)用Condition的matches方法判斷是否進行注冊成Bean。從而實現(xiàn)各種復(fù)雜的動態(tài)判斷注冊成Bean的情況。

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

相關(guān)文章

  • Java關(guān)于桶排序的知識點總結(jié)

    Java關(guān)于桶排序的知識點總結(jié)

    這篇文章給大家總結(jié)了關(guān)于JAVA中J桶排序的相關(guān)知識點和用法分享,有興趣的讀者跟著學(xué)習(xí)下。
    2018-04-04
  • Java編程實現(xiàn)排他鎖代碼詳解

    Java編程實現(xiàn)排他鎖代碼詳解

    這篇文章主要介紹了Java編程實現(xiàn)排他鎖的相關(guān)內(nèi)容,敘述了實現(xiàn)此代碼鎖所需要的功能,以及作者的解決方案,然后向大家分享了設(shè)計源碼,需要的朋友可以參考下。
    2017-10-10
  • Java中IO流解析及代碼實例

    Java中IO流解析及代碼實例

    下面小編就為大家?guī)硪黄P(guān)于Java中的IO流總結(jié)(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-07-07
  • SpringBoot基于MyBatis-Plus實現(xiàn)Lambda Query查詢的示例代碼

    SpringBoot基于MyBatis-Plus實現(xiàn)Lambda Query查詢的示例代碼

    MyBatis-Plus 是 MyBatis 的增強工具,簡化了數(shù)據(jù)庫操作,并提高了開發(fā)效率,它提供了多種查詢方式,包括常規(guī)的 SQL 查詢、Lambda Query 查詢、分頁查詢、條件查詢等,在本篇博客中,我們將詳細講解如何使用 MyBatis-Plus 的各種查詢方式,需要的朋友可以參考下
    2025-01-01
  • 一文詳解JavaWeb過濾器(Filter)

    一文詳解JavaWeb過濾器(Filter)

    本文主要介紹了一文詳解JavaWeb過濾器(Filter),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • SpringBoot使用Hibernate攔截器實現(xiàn)時間自動注入的操作代碼

    SpringBoot使用Hibernate攔截器實現(xiàn)時間自動注入的操作代碼

    這篇文章主要介紹了SpringBoot使用Hibernate攔截器實現(xiàn)時間自動注入的操作代碼,主要包括hibernate攔截器的相關(guān)知識,結(jié)合實例代碼給大家講解的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-10-10
  • 使用Java發(fā)送郵件到QQ郵箱的完整指南

    使用Java發(fā)送郵件到QQ郵箱的完整指南

    在現(xiàn)代軟件開發(fā)中,郵件發(fā)送功能是一個常見的需求,無論是用戶注冊驗證、密碼重置,還是系統(tǒng)通知,郵件都是一種重要的通信方式,本文將詳細介紹如何使用Java編寫程序,實現(xiàn)發(fā)送郵件到QQ郵箱的功能,需要的朋友可以參考下
    2025-03-03
  • java使用JDBC動態(tài)創(chuàng)建數(shù)據(jù)表及SQL預(yù)處理的方法

    java使用JDBC動態(tài)創(chuàng)建數(shù)據(jù)表及SQL預(yù)處理的方法

    這篇文章主要介紹了java使用JDBC動態(tài)創(chuàng)建數(shù)據(jù)表及SQL預(yù)處理的方法,涉及JDBC操作數(shù)據(jù)庫的連接、創(chuàng)建表、添加數(shù)據(jù)、查詢等相關(guān)實現(xiàn)技巧,需要的朋友可以參考下
    2017-08-08
  • java基于雙向環(huán)形鏈表解決丟手帕問題的方法示例

    java基于雙向環(huán)形鏈表解決丟手帕問題的方法示例

    這篇文章主要介紹了java基于雙向環(huán)形鏈表解決丟手帕問題的方法,簡單描述了丟手帕問題,并結(jié)合實例形式給出了Java基于雙向環(huán)形鏈表解決丟手帕問題的步驟與相關(guān)操作技巧,需要的朋友可以參考下
    2017-11-11
  • spring-AOP 及 AOP獲取request各項參數(shù)操作

    spring-AOP 及 AOP獲取request各項參數(shù)操作

    這篇文章主要介紹了spring-AOP 及 AOP獲取request各項參數(shù)的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評論