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

Spring條件注解沒生效該如何解決

 更新時(shí)間:2023年09月11日 14:54:53   作者:江南一點(diǎn)雨  
條件注解相信各位小伙伴都用過,Spring?中的多環(huán)境配置?profile?底層就是通過條件注解來實(shí)現(xiàn)的,下面小編就來為大家介紹一下當(dāng)Spring條件注解沒生效時(shí)該如何解決,感興趣的可以了解下

從 Spring4.0 開始,Spring 提供了一個(gè)更加細(xì)粒度的條件注解: ConfigurationCondition。從名字上就可以看出來這個(gè)是搭配 @Configuration 注解一起使用的,ConfigurationCondition 提供了一種更加細(xì)粒度的條件匹配,可以在配置或者 Bean 注冊(cè)的時(shí)候去評(píng)估條件注解是否滿足。

也就是說,當(dāng)一個(gè)類上存在條件注解的時(shí)候,我們可以有兩個(gè)評(píng)估條件注解是否滿足的時(shí)機(jī):

  • 在配置的時(shí)候去評(píng)估。
  • 在 Bean 注冊(cè)的時(shí)候評(píng)估。

在配置的時(shí)候評(píng)估,可能會(huì)導(dǎo)致當(dāng)前類都不會(huì)被加載,在 Bean 注冊(cè)的時(shí)候再去評(píng)估,意味著當(dāng)前類就會(huì)被加載。

1. ConfigurationCondition

我們先來看下這個(gè)類的定義:

public interface ConfigurationCondition extends Condition {
	ConfigurationPhase getConfigurationPhase();
	enum ConfigurationPhase {
		PARSE_CONFIGURATION,
		REGISTER_BEAN
	}
}

大家看到,這里其實(shí)就是定義了兩個(gè)枚舉值,然后提供了一個(gè)方法返回枚舉值。

  • PARSE_CONFIGURATION:這個(gè)表示 Condition 條件應(yīng)該在解析 @Configuration 類時(shí)進(jìn)行評(píng)估,如果評(píng)估不通過,則不會(huì)將 @Configuration 添加到容器中。
  • REGISTER_BEAN:這個(gè)表示添加常規(guī) Bean 的時(shí)候去評(píng)估 Condition 條件(常規(guī) Bean 就是指非配置類,例如添加搭配 @Bean 注解使用的條件注解),這個(gè)條件不會(huì)阻止注冊(cè) @Configuration 類到容器中。

其實(shí)道理很好懂,就是加載配置類的時(shí)候就根據(jù)條件注解判斷要不要加載配置類,還是等到注冊(cè) Bean 的時(shí)候再去看條件注解是否滿足條件。

2. 案例分析

松哥通過一個(gè)簡(jiǎn)單案例來和小伙伴們演示一下。

假設(shè)我現(xiàn)在有如下條件:

public class MyCondition implements ConfigurationCondition {
    @Override
    public ConfigurationPhase getConfigurationPhase() {
        return ConfigurationPhase.PARSE_CONFIGURATION;
    }
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getBeanFactory().containsBean("a");
    }
}

這個(gè)條件我沒有直接實(shí)現(xiàn) Condition 接口,而是實(shí)現(xiàn)類 ConfigurationCondition 接口,在這個(gè)接口中,getConfigurationPhase 方法返回了 PARSE_CONFIGURATION,表示在加載配置類的時(shí)候就去評(píng)估條件是否滿足,matches 方法則是去判斷容器中是否存在一個(gè)名為 a 的 Bean。

現(xiàn)在我有兩個(gè)配置類,分別是 A 和 B,如下:

@Configuration
public class A {
}
@Configuration
@Conditional(MyCondition.class)
public class B {
}

A 配置類正常加載,B 配置類有一個(gè)加載條件,就是得 A 存在,B 才會(huì)加載。

現(xiàn)在,在容器中加載 B 和 A 兩個(gè)配置,如下:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(B.class,A.class);
ctx.refresh();
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}

大家注意,加載的時(shí)候,我先加載了 B,后加載了 A,這點(diǎn)很重要,加載 B 的時(shí)候,由于此時(shí)容器中還不存在一個(gè)名為 a 的 Bean,而我們的評(píng)估時(shí)機(jī)是在處理配置類的時(shí)候,因此就會(huì)導(dǎo)致 B 配置類不會(huì)被加載,最終打印出來的 BeanName 就沒有 b。

但是,如果我們將 MyCondition 中,條件注解的評(píng)估時(shí)機(jī)改為 ConfigurationPhase.REGISTER_BEAN,那么就表示在系統(tǒng)啟動(dòng)的時(shí)候,并不會(huì)去評(píng)估條件注解是否滿足,而是會(huì)將 @Configuration 配置類進(jìn)行解析,此時(shí)啟動(dòng)系統(tǒng),就會(huì)發(fā)現(xiàn)最終打印出來的 beanName 里既有 a 又有 b。

3. 源碼分析

接下來我們?cè)賮韽脑创a的角度來分析一下上述行為。

在 Spring 中,提供了一個(gè)專門的內(nèi)部類 ConditionEvaluator 來處理要不要跳過條件注解,該類中有一個(gè)名為 shouldSkip 的方法,用來處理此事:

public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
	return shouldSkip(metadata, null);
}
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
	if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
		return false;
	}
	if (phase == null) {
		if (metadata instanceof AnnotationMetadata annotationMetadata &&
				ConfigurationClassUtils.isConfigurationCandidate(annotationMetadata)) {
			return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
		}
		return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
	}
	List<Condition> conditions = new ArrayList<>();
	for (String[] conditionClasses : getConditionClasses(metadata)) {
		for (String conditionClass : conditionClasses) {
			Condition condition = getCondition(conditionClass, this.context.getClassLoader());
			conditions.add(condition);
		}
	}
	AnnotationAwareOrderComparator.sort(conditions);
	for (Condition condition : conditions) {
		ConfigurationPhase requiredPhase = null;
		if (condition instanceof ConfigurationCondition configurationCondition) {
			requiredPhase = configurationCondition.getConfigurationPhase();
		}
		if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
			return true;
		}
	}
	return false;
}

第一個(gè)方法不用多說,我們來看第二個(gè)重載方法,重載方法多了一個(gè)參數(shù) ConfigurationPhase,這個(gè)就表示配置的階段,也就是條件注解生效的階段。

首先會(huì)去判斷當(dāng)前注解是否是一個(gè)條件注解,如果不是條件注意,那么就不能跳過,要繼續(xù)后面的解析(繼續(xù)后面的解析時(shí) Bean 將會(huì)被注冊(cè)),如果是條件注解,則繼續(xù)后面的判斷。繼續(xù)判斷,如果沒有傳遞 phase 進(jìn)來,說明沒有指定應(yīng)該在哪個(gè)階段去評(píng)估條件注解,那么這個(gè)時(shí)候就去判斷,如果當(dāng)前注解是一個(gè)配置類上的注解,那么就設(shè)置 phase 為 PARSE_CONFIGURATION,然后繼續(xù)調(diào)用 shouldSkip 方法,否則就設(shè)置 phase 為 REGISTER_BEAN 然后繼續(xù)調(diào)用 shouldSkip 方法。

那么什么樣的情況會(huì)被認(rèn)為是一個(gè)配置類上的注解呢?如果當(dāng)前類上添加的注解時(shí) @Component、@ComponentScan、@Import、@ImportResource 以及這四種注解衍生出來的注解,亦或者當(dāng)前類中有 @Bean 注解標(biāo)記的方法,那么當(dāng)前類就是一個(gè)配置類,就會(huì)設(shè)置 phase 為 PARSE_CONFIGURATION。

第二次進(jìn)入 shouldSkip 方法的時(shí)候,就已經(jīng)有明確的 phase 了。這次進(jìn)來后,把所有的條件注解的條件收集起來,存入到 conditions 集合中,然后再對(duì)該集合進(jìn)行排序。然后遍歷該集合。遍歷的時(shí)候就去判斷這個(gè)條件注解是不是 ConfigurationCondition 類型的,如果是,則提取出來其中的 phase 為 requiredPhase,這個(gè)就表示這個(gè)條件注意希望自己被處理的階段,接下來去判斷,如果 requiredPhase 為空,說明條件并未指定自己的執(zhí)行時(shí)間,那么就執(zhí)行 matches 方法進(jìn)行條件評(píng)估;如果 requiredPhase 不為空,并且和傳入的 phase 相等,那么也是當(dāng)前評(píng)估。其實(shí)這個(gè)判斷核心邏輯就是以參數(shù)傳入進(jìn)來的 phase 為準(zhǔn),要么條件沒有設(shè)置評(píng)估時(shí)機(jī),要么設(shè)置了,但是得和參數(shù)傳進(jìn)來的 phase 一致,只有滿足這兩個(gè)條件,才會(huì)當(dāng)場(chǎng)進(jìn)行評(píng)估。

這就是系統(tǒng)條件注解的評(píng)估邏輯。

對(duì)于配置類來說,是在 AnnotatedBeanDefinitionReader#doRegisterBean 方法中調(diào)用評(píng)估邏輯的:

private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
		@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
		@Nullable BeanDefinitionCustomizer[] customizers) {
	AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
	if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
		return;
	}
    //...
}

調(diào)用的時(shí)候并未明確指定 phase,所以會(huì)在進(jìn)入到 shouldSkip 方法后,自行分析是哪個(gè)階段評(píng)估條件注解。

對(duì)于 @Bean 注解標(biāo)記的類來說,是在 ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod 方法中調(diào)用評(píng)估邏輯的:

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
	ConfigurationClass configClass = beanMethod.getConfigurationClass();
	MethodMetadata metadata = beanMethod.getMetadata();
	String methodName = metadata.getMethodName();
	if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
		configClass.skippedBeanMethods.add(methodName);
		return;
	}
    //...
}

這個(gè)調(diào)用的時(shí)候,就傳入了 phase 了,直接指定了是在 Bean 初始化的時(shí)候評(píng)估。

好啦,這就是條件注解條件評(píng)估時(shí)機(jī)的兩種情況。在 Spring Boot 中定義的條件注解里,有不少都用到了 ConfigurationCondition,而不是傳統(tǒng)的 Condition,感興趣的小伙伴可以自行查看哦~

到此這篇關(guān)于Spring條件注解沒生效該如何解決的文章就介紹到這了,更多相關(guān)Spring條件注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • idea mybatis配置log4j打印sql語句的示例

    idea mybatis配置log4j打印sql語句的示例

    本篇文章主要介紹了idea mybatis配置log4j打印sql語句的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-01-01
  • SpringBoot JPA使用配置過程詳解

    SpringBoot JPA使用配置過程詳解

    這篇文章主要介紹了SpringBoot JPA使用配置過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • Springcloud中Feign傳遞參數(shù)的過程解析

    Springcloud中Feign傳遞參數(shù)的過程解析

    這篇文章主要介紹了Springcloud中Feign傳遞參數(shù)的過程,單個(gè)參數(shù)的傳值有兩種方式,第一種使用@RequestParam/@PathVariable進(jìn)行傳值,傳遞多個(gè)參數(shù):多個(gè)參數(shù)的傳值可以使用多個(gè)@RequestParam來進(jìn)行傳參,需要的朋友可以參考下
    2023-09-09
  • Springboot+rabbitmq實(shí)現(xiàn)延時(shí)隊(duì)列的兩種方式

    Springboot+rabbitmq實(shí)現(xiàn)延時(shí)隊(duì)列的兩種方式

    這篇文章主要介紹了Springboot+rabbitmq實(shí)現(xiàn)延時(shí)隊(duì)列的兩種方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • 將Java(SpringBoot)項(xiàng)目打包為Docker鏡像的三種方法

    將Java(SpringBoot)項(xiàng)目打包為Docker鏡像的三種方法

    這篇文章主要介紹了將Java(SpringBoot)項(xiàng)目打包為Docker鏡像的三種方法,分別是手動(dòng)構(gòu)建、使用Dockerfile和使用SpringBootMaven插件,每種方法都有其特點(diǎn)和適用場(chǎng)景,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-03-03
  • 使用Java代碼將IP地址轉(zhuǎn)換為int類型的方法

    使用Java代碼將IP地址轉(zhuǎn)換為int類型的方法

    這篇文章主要介紹了使用Java代碼將IP地址轉(zhuǎn)換為int類型的方法,這也是各大計(jì)算機(jī)考試和ACM以及面試的常見基礎(chǔ)問題,需要的朋友可以參考下
    2015-08-08
  • 基于javax.validation結(jié)合spring的最佳實(shí)踐

    基于javax.validation結(jié)合spring的最佳實(shí)踐

    這篇文章主要介紹了javax.validation結(jié)合spring的最佳實(shí)踐,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java中的while循環(huán)語句詳細(xì)講解

    Java中的while循環(huán)語句詳細(xì)講解

    這篇文章主要給大家介紹了關(guān)于Java中while循環(huán)語句的相關(guān)資料,while循環(huán)是一種在編程中常見的控制流語句,它允許代碼在特定條件下(通常是一個(gè)布爾表達(dá)式)重復(fù)執(zhí)行一段代碼,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-03-03
  • Java利用Spire.PDF實(shí)現(xiàn)將PDF文檔轉(zhuǎn)換為Word格式

    Java利用Spire.PDF實(shí)現(xiàn)將PDF文檔轉(zhuǎn)換為Word格式

    在日常工作和學(xué)習(xí)中,我們經(jīng)常會(huì)遇到PDF文檔,本文將詳細(xì)介紹如何才能高效、精準(zhǔn)地使用Java實(shí)現(xiàn)PDF到Word的自動(dòng)化轉(zhuǎn)換,感興趣的小伙伴可以了解下
    2025-09-09
  • springboot實(shí)現(xiàn)打印彩色日志

    springboot實(shí)現(xiàn)打印彩色日志

    這篇文章主要介紹了springboot實(shí)現(xiàn)打印彩色日志的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評(píng)論

永福县| 莎车县| 高阳县| 保亭| 楚雄市| 大丰市| 翁牛特旗| 昂仁县| 垣曲县| 贵溪市| 华亭县| 汨罗市| 凤城市| 会理县| 石林| 凤庆县| 津南区| 晋州市| 巴南区| 阳山县| 莱西市| 盘山县| 湘阴县| 淮北市| 巩留县| 芦山县| 上犹县| 清水河县| 盐池县| 隆回县| 蒙自县| 柘荣县| 武安市| 沭阳县| 博白县| 和田市| 田林县| 娱乐| 道孚县| 伊通| 灯塔市|