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

關(guān)于ConditionalOnMissingBean失效問題的追蹤

 更新時(shí)間:2022年03月02日 15:37:33   作者:伊布拉西莫  
這篇文章主要介紹了關(guān)于ConditionalOnMissingBean失效問題的追蹤方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

遇到一個(gè)@ConditionalOnMissingBean失效的問題,今天花點(diǎn)時(shí)間來分析一下。

現(xiàn)場(chǎng)回放

services

首先介紹下代碼結(jié)構(gòu):有RunService,以及它的兩個(gè)實(shí)現(xiàn)類:TrainRunServiceImpl和CarRunServiceImpl

RunService

public interface RunService {
? ? void run();
}

TrainRunServiceImpl

public class TrainRunServiceImpl implements RunService {
? ? @Override
? ? public void run() {
? ? ? ? System.out.println("開火車,wuwuwuwuwu");
? ? }
}

CarRunServiceImpl

public class CarRunServiceImpl implements RunService {
? ? @Override
? ? public void run() {
? ? ? ? System.out.println("汽車,dididi");
? ? }
}

操作類

操作類MyInitBean中,注入了RunService – byType

@Component
public class MyInitBean implements InitializingBean {
    @Autowired
    private RunService runService;
    @Override
    public void afterPropertiesSet() throws Exception {
        runService.run();
    }
}

configuration

我們?cè)谂渲妙愔校⑷隦unService的實(shí)現(xiàn)bean,并通過@ConditionalOnMissingBean來判斷是否注入。

@Configuration
public class MyConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public RunService carRunServiceImpl() {
        return new CarRunServiceImpl();
    }
    
    @Bean
    public RunService trainRunServiceImpl() {
        return new TrainRunServiceImpl();
    }
}

拋出異常

按照上述的代碼,執(zhí)行后,本以為會(huì)成功執(zhí)行,但是卻拋出了異常,異常信息如下:

在spring容器中存在了兩個(gè)RunService實(shí)現(xiàn)類。

這導(dǎo)致了MyInitBean無法決定它到底該使用這兩個(gè)中的哪一個(gè)。(默認(rèn)是byType注入的)

按照上述的異常信息,它給出了兩種解決方案:

@Qualifier

在注入bean時(shí),指定bean的名稱.

@Controller
public class MyInitBean implements InitializingBean {
? ? @Autowired
? ? @Qualifier("carRunServiceImpl")
? ? private RunService runService;
}

通過@Configuration配置類注入的bean,默認(rèn)名稱為方法名稱

? @Bean // ?`trainRunServiceImpl `
? public RunService trainRunServiceImpl() {
? ? ?return new TrainRunServiceImpl();
?}

直接在類頭部申明注入的bean,默認(rèn)名稱為類名稱

@Service ?// ?`trainRunServiceImpl`
public class TrainRunServiceImpl implements RunService {
}

@Primary

@Primary的作用是,在bean存在多個(gè)候選者且無法決定使用哪一個(gè)時(shí),優(yōu)先使用帶有該注解的bean.

在配置類中Configuration添加

? @Bean
? @Primary
? public RunService trainRunServiceImpl() {
? ? ? return new TrainRunServiceImpl();
? }

在類申明中添加

@Primary
public class TrainRunServiceImpl implements RunService {
}

注意

在上述給出的兩種方法中,無論是使用@Primary還是這里容器中仍然存在多個(gè)實(shí)現(xiàn)類,

這并不是我們想要的結(jié)果。

這里為什么@ConditionalOnMissingBean會(huì)失效呢?

問題定位

在進(jìn)行問題定位前,我們先來回顧一下@ConditionalOnMissingBean的工作原理

工作原理

@ConditionalOnMissingBean

ConditionalOnMissingBean的注解定義如下:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {
?? ?Class<?>[] value() default {};
?? ?String[] type() default {};
?? ?
?? ?//略....
}

@ConditionalOnMissingBean通??梢杂腥缦氯N使用方式:

? ? @Bean
// ? ?@ConditionalOnMissingBean(type ="xxx.yyy.zzz.service")
// ? ?@ConditionalOnMissingBean(value = RunService.class)
? ? @ConditionalOnMissingBean //無參數(shù),表示按照返回值類型過濾
? ? public RunService carRunServiceImpl() {
? ? ? ? return new CarRunServiceImpl();
? ? }

在注解上看到了一個(gè)OnBeanCondition類,在@ConditionalOnBean,ConditionalOnSingleCandidate和ConditionalOnMissingBean都看到了它的身影。

OnBeanCondition

@Order(Ordered.LOWEST_PRECEDENCE)
class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition {
?? ?@Override
?? ?public ConditionOutcome getMatchOutcome(ConditionContext context,
?? ??? ??? ?AnnotatedTypeMetadata metadata) {
?? ??? ?//ConditionalOnBean ?略
?? ??? ?//ConditionalOnSingleCandidate 略
?? ??? ?
?? ??? ?if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
?? ??? ??? ?//尋找 @ConditionalOnMissingBean 匹配的 type;
?? ??? ??? ?BeanSearchSpec spec = new BeanSearchSpec(context, metadata,ConditionalOnMissingBean.class);
?? ??? ??? ?//從容器中尋找指定的type --- ?step1
?? ??? ??? ?MatchResult matchResult = getMatchingBeans(context, spec);
?? ??? ??? ?if (matchResult.isAnyMatched()) {
?? ??? ??? ??? ?//如果存在指定的type
?? ??? ??? ??? ?//reason: ?found beans of type 'service.Service' AServiceImpl
?? ??? ??? ??? ?String reason = createOnMissingBeanNoMatchReason(matchResult);
?? ??? ??? ??? ?//創(chuàng)建 ConditionOutcome.noMatch: return new ConditionOutcome(false, message);
?? ??? ??? ??? ?return ConditionOutcome.noMatch(ConditionMessage
?? ??? ??? ??? ??? ??? ?.forCondition(ConditionalOnMissingBean.class, spec)
?? ??? ??? ??? ??? ??? ?.because(reason));
?? ??? ??? ?}
?? ??? ??? ?
?? ??? ??? ?matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, spec)
?? ??? ??? ??? ??? ?.didNotFind("any beans").atAll();
?? ??? ?}
?? ??? ?//默認(rèn) 創(chuàng)建 ConditionOutcome.match : return new ConditionOutcome(true, message);
?? ??? ?return ConditionOutcome.match(matchMessage);
?? ?}
}

ConditionOutcome 的用法:當(dāng)match= true時(shí),才注入容器.

若@ConditionalOnMissingBean找到了匹配項(xiàng),則返回ConditionOutcome.notMatch,則不注入容器。

問題出在哪? 

有了上面的一系列原理支撐,但是為什么沒有執(zhí)行到我們想要的結(jié)果呢?

debug執(zhí)行后,發(fā)現(xiàn)問題出現(xiàn)在OnBeanCondition .getMatchingBeans(context, spec)這個(gè)方法中。

首先再次回顧下配置類:

在注入carRunServiceImpl時(shí),執(zhí)行OnBeanCondition .getMatchingBeans(context, spec)并沒有找到下面定義的trainRunServiceImpl.

真相只有一個(gè):

@Configuration 在初始化bean的時(shí)候,順序出現(xiàn)了問題,那么如何控制初始化bean的順序呢?

解決問題

一頓分析之后,我們發(fā)現(xiàn)只要控制了bean的加載順序之后,上述的問題就可以解決了。

接下來我們來嘗試控制bean初始化順序:

Configuration中bean使用@Order ----------------- failure

@Configuration
public class MyConfiguration {
    @Order(2)
    @Bean
    @ConditionalOnMissingBean
    public RunService carRunServiceImpl() {
        return new CarRunServiceImpl();
    }
    @Order(1)
    @Bean
    public RunService trainRunServiceImpl() {
        return new TrainRunServiceImpl();
    }
}

Configuration 調(diào)整bean申明順序----------------- success

將帶有@ConditionalOnMissingBean注解的bean,申明在代碼的末尾位置,操作成功:

@Configuration
public class MyConfiguration {
	@Bean
    public RunService trainRunServiceImpl() {
        return new TrainRunServiceImpl();
    }
    @Bean
    @ConditionalOnMissingBean
    public RunService carRunServiceImpl() {
        return new CarRunServiceImpl();
    }
}

配置多個(gè)Configuration類,并通過@Order指定順序---------------- failure

@Configuration
@Order(Ordered.LOWEST_PRECEDENCE) //最低優(yōu)先級(jí)
public class MyConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public RunService carRunServiceImpl() {
        return new CarRunServiceImpl();
    }
}
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE) //最高優(yōu)先級(jí)
public class MyConfiguration2 {
    @Bean
    public RunService trainRunServiceImpl() {
        return new TrainRunServiceImpl();
    }
}

@Configuration并不能通過@Order指定順序。

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

@Configuration配置類加載順序通過類名順序來加載 ------- 驗(yàn)證success

將MyConfiguration2重命名為Configuration2,而它的加載順序在MyConfiguration之前,執(zhí)行程序成功。

這里貌似所有的問題似乎都解決了, 只需要我們自定義的配置類名稱保證最優(yōu)先加載就可以了。我們只需要注意配置類的命名規(guī)則即可.

但是,這種解決方案,似乎并不是那么令人信服。

@AutoConfigureBefore,@AutoConfigureAfter

經(jīng)查文檔,終于找到了需要的東西:我們可以通過@AutoConfigureBefore,@AutoConfigureAfter來控制配置類的加載順序。

@Configuration
public class MyConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public RunService carRunServiceImpl() {
        return new CarRunServiceImpl();
    }
}
@Configuration
@AutoConfigureBefore(MyConfiguration.class)
public class MyConfiguration2 {
    @Bean
    public RunService trainRunServiceImpl() {
        return new TrainRunServiceImpl();
    }
}

注意:

如果要開啟@EnableAutoConfiguration需要在META-INF/spring.factories文件中添加如下內(nèi)容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xxx.configuration.MyConfiguration2,\
xxx.configuration.MyConfiguration

結(jié)論

我們需要控制目標(biāo)bean的加載順序即可。

但是我們?cè)趯?shí)際的使用一些通用plugin過程中(如redis),并沒有刻意的指定bean的加載順序,這是為什么呢?

因?yàn)椋?/strong>在實(shí)際的應(yīng)用過程中,我們使用第三方插件,他們的默認(rèn)配置都會(huì)存在于插件的jar包中,而我們的個(gè)性化配置則存在于自身的應(yīng)用中。

而容器會(huì)優(yōu)先執(zhí)行classes/,然后才執(zhí)行jars/classes.

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringCloud高可用配置中心Config詳解

    SpringCloud高可用配置中心Config詳解

    Spring Cloud Config 是一個(gè)解決分布式系統(tǒng)的配置管理方案,它包含了 server 和 client 兩個(gè)部分,這篇文章主要介紹了SpringCloud之配置中心Config(高可用),需要的朋友可以參考下
    2022-04-04
  • 學(xué)生信息管理系統(tǒng)java版

    學(xué)生信息管理系統(tǒng)java版

    這篇文章主要為大家詳細(xì)介紹了java學(xué)生信息管理系統(tǒng)源代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • ManyToMany單向、雙向:@JoinTable的使用

    ManyToMany單向、雙向:@JoinTable的使用

    這篇文章主要介紹了ManyToMany單向、雙向:@JoinTable的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • MyBatis-Flex BaseMapper的接口基本用法小結(jié)

    MyBatis-Flex BaseMapper的接口基本用法小結(jié)

    本文主要介紹了MyBatis-Flex BaseMapper的接口基本用法小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • 基于JWT實(shí)現(xiàn)SSO單點(diǎn)登錄流程圖解

    基于JWT實(shí)現(xiàn)SSO單點(diǎn)登錄流程圖解

    這篇文章主要介紹了基于JWT實(shí)現(xiàn)SSO單點(diǎn)登錄流程圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • IDEA使用入門小白操作教程

    IDEA使用入門小白操作教程

    IntelliJ IDEA,就是Java語(yǔ)言開發(fā)工具 (IDE) 功能與Eclipse同類!本文通過圖文并茂的形式給大家介紹IntelliJ IDEA使用入門教程,非常適合新手小白操作,感興趣的朋友一起看看吧
    2020-10-10
  • SpringBoot中API接口參數(shù)獲取方式小結(jié)

    SpringBoot中API接口參數(shù)獲取方式小結(jié)

    在Spring Boot中,API接口參數(shù)可以通過多種方式獲取,具體取決于你定義的API接口參數(shù)類型(如路徑參數(shù)、查詢參數(shù)、請(qǐng)求體參數(shù)、請(qǐng)求頭等),本文給大家就介紹了一些常見的參數(shù)獲取方式,需要的朋友可以參考下
    2024-06-06
  • java實(shí)現(xiàn)快速打字游戲

    java實(shí)現(xiàn)快速打字游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)快速打字游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • Java類加載器層次結(jié)構(gòu)原理解析

    Java類加載器層次結(jié)構(gòu)原理解析

    這篇文章主要介紹了Java類加載器層次結(jié)構(gòu)原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • Java 并發(fā)編程學(xué)習(xí)筆記之核心理論基礎(chǔ)

    Java 并發(fā)編程學(xué)習(xí)筆記之核心理論基礎(chǔ)

    編寫優(yōu)質(zhì)的并發(fā)代碼是一件難度極高的事情。Java語(yǔ)言從第一版本開始內(nèi)置了對(duì)多線程的支持,這一點(diǎn)在當(dāng)年是非常了不起的,但是當(dāng)我們對(duì)并發(fā)編程有了更深刻的認(rèn)識(shí)和更多的實(shí)踐后,實(shí)現(xiàn)并發(fā)編程就有了更多的方案和更好的選擇。本文是對(duì)并發(fā)編程的核心理論做了下小結(jié)
    2016-05-05

最新評(píng)論

黄大仙区| 巩留县| 涿州市| 涞源县| 修水县| 宁明县| 浦县| 通山县| 胶州市| 美姑县| 永川市| 美姑县| 乌鲁木齐县| 成武县| 延庆县| 宝山区| 揭西县| 瑞安市| 乐业县| 天门市| 鱼台县| 竹北市| 房山区| 南漳县| 吉首市| 黄平县| 堆龙德庆县| 积石山| 崇文区| 台南市| 潮安县| 清苑县| 富顺县| 凤阳县| 崇明县| 镇远县| 思茅市| 周口市| 丹凤县| 自贡市| 马龙县|