最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

@Conditional是Spring 4出現(xiàn)的注解,但是真正露出價(jià)值的是Spring Boot的擴(kuò)展@ConditionalOnBean等。但是任然使用的是Spring框架進(jìn)行處理,并沒有做太多定制的東西,所以還是先看看@Conditional注解的實(shí)現(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)比較簡(jiǎn)單,只需要定義一個(gè)Condition子類即可,并且說明只有滿足了當(dāng)前Condition的matches方法時(shí)才會(huì)將當(dāng)前@Component注冊(cè)成Bean。那么再看看Condition接口和體系。

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

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

ConfigurationClassParser # processConfigurationClass (ConfigurationClassParser # doProcessConfigurationClass等)

ConditionEvaluator # shouldSkip

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

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

大致有四類

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

2)、ConfigurationCondition

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

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

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

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

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

由于情況比較復(fù)雜,可能@Component上有@ComponentScan,則會(huì)遞歸進(jìn)行處理,總之都會(huì)先調(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時(shí)候,就初始化了內(nèi)部類ConditionContextImpl,并且傳入了BeanFactory(Bean是否存在可以通過工廠進(jìn)行判斷)、Environment(環(huán)境配置、Profile等存放在其中)、ResourceLoader(Spring的Resource加載器)、ClassLoader(類加載器)等。

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

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

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

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

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    // 注解信息不能為空, 并且必須有Conditional或者其子類
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
        return false;
    }
    // 如果判斷階段為空,進(jìn)行類型判斷再遞歸調(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同時(shí)需要多個(gè)條件成立
    for (String[] conditionClasses : getConditionClasses(metadata)) {
        for (String conditionClass : conditionClasses) {
            Condition condition = getCondition(conditionClass, this.context.getClassLoader());
            conditions.add(condition);
        }
    }
    // 條件排序(根據(jù)Spring的那三個(gè)排序方式定義)
    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)、進(jìn)行排序

5)、遍歷Condition,是否在該階段進(jìn)行判斷。需要?jiǎng)t再調(diào)用該Condition的matches方法

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

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

相關(guān)文章

  • Spring Boot 4 與 Spring Framework 7新特性、升級(jí)要點(diǎn)與實(shí)戰(zhàn)指南

    Spring Boot 4 與 Spring Framework&nb

    SpringBoot4和SpringFramework7帶來了大量新特性,包括升級(jí)到JakartaEE11、支持Java21、增強(qiáng)模塊化、優(yōu)化測(cè)試、增加彈性功能等,本文介紹Spring Boot 4 與 Spring Framework 7新特性、升級(jí)要點(diǎn)與實(shí)戰(zhàn)指南,感興趣的朋友跟隨小編一起看看吧
    2026-03-03
  • java+mysql實(shí)現(xiàn)登錄和注冊(cè)功能

    java+mysql實(shí)現(xiàn)登錄和注冊(cè)功能

    這篇文章主要為大家詳細(xì)介紹了java+mysql實(shí)現(xiàn)登錄和注冊(cè)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • java實(shí)現(xiàn)時(shí)鐘表盤

    java實(shí)現(xiàn)時(shí)鐘表盤

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)時(shí)鐘表盤,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • springboot部署linux訪問服務(wù)器資源的方法

    springboot部署linux訪問服務(wù)器資源的方法

    這篇文章主要介紹了springboot部署linux訪問服務(wù)器資源,部署springboot項(xiàng)目至服務(wù)器用了幾種不同方法,文中給大家詳細(xì)介紹,需要的朋友可以參考下
    2019-12-12
  • Java實(shí)現(xiàn)時(shí)間戳轉(zhuǎn)代碼運(yùn)行時(shí)長(zhǎng)

    Java實(shí)現(xiàn)時(shí)間戳轉(zhuǎn)代碼運(yùn)行時(shí)長(zhǎng)

    這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)時(shí)間戳轉(zhuǎn)代碼運(yùn)行時(shí)長(zhǎng)功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-06-06
  • Java限流方法常見實(shí)現(xiàn)方案(單機(jī)限流和分布式限流)

    Java限流方法常見實(shí)現(xiàn)方案(單機(jī)限流和分布式限流)

    Java限流用于保護(hù)系統(tǒng)資源,分為單機(jī)(Guava/滑動(dòng)窗口)和分布式(Redis+Lua)方案,核心算法包括固定窗口、令牌桶、漏桶等,推薦使用Sentinel等成熟框架實(shí)現(xiàn)動(dòng)態(tài)流量控制,本文介紹Java限流方法常見實(shí)現(xiàn)方案(單機(jī)限流和分布式限流),感興趣的朋友一起看看吧
    2025-08-08
  • Java?Stream流以及常用方法操作實(shí)例

    Java?Stream流以及常用方法操作實(shí)例

    Stream是對(duì)Java中集合的一種增強(qiáng)方式,使用它可以將集合的處理過程變得更加簡(jiǎn)潔、高效和易讀,這篇文章主要介紹了Java?Stream流以及常用方法的相關(guān)資料,需要的朋友可以參考下
    2025-08-08
  • SpringBoot文件上傳接口并發(fā)性能調(diào)優(yōu)

    SpringBoot文件上傳接口并發(fā)性能調(diào)優(yōu)

    在一個(gè)項(xiàng)目現(xiàn)場(chǎng),文件上傳接口(文件500K)QPS只有30,這個(gè)并發(fā)性能確實(shí)堪憂,此文記錄出坑過程,文中通過代碼示例講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2024-06-06
  • Java Set 中的常用方法示例總結(jié)

    Java Set 中的常用方法示例總結(jié)

    Set是Java集合框架(Collection Framework)中最常用的接口之一,具有元素唯一、不允許重復(fù) 的特性,這篇文章將全面總結(jié)Java Set的常用方法并通過示例進(jìn)行講解,主要用于日常查詢使用,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • Java Math.round(),Math.ceil(),Math.floor()的區(qū)別詳解

    Java Math.round(),Math.ceil(),Math.floor()的區(qū)別詳解

    這篇文章主要介紹了Java Math.round(),Math.ceil(),Math.floor()的區(qū)別詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08

最新評(píng)論

咸宁市| 临洮县| 凉山| 五原县| 永仁县| 曲阳县| 莆田市| 连州市| 镇康县| 西吉县| 正镶白旗| 合阳县| 西华县| 礼泉县| 革吉县| 敦煌市| 惠州市| 本溪市| 克山县| 绥芬河市| 威远县| 通榆县| 留坝县| 永修县| 高平市| 隆林| 谢通门县| 和平县| 固镇县| 海丰县| 玉门市| 土默特右旗| 龙里县| 营口市| 南乐县| 临颍县| 双桥区| 漳平市| 平塘县| 闸北区| 黑山县|