成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

二方庫開發(fā)過程中防止bean沖突的思考

HelKyle / 2995人閱讀

摘要:如何解決非集成情況下可能存在沖突的問題,有以下三種方案強(qiáng)制業(yè)務(wù)系統(tǒng)集成出現(xiàn)沖突時(shí)使用標(biāo)明其自己已存在的沖突,以防止按注入出現(xiàn)的沖突異常。

我們開發(fā)內(nèi)部用的二方庫時(shí)往往需要定義一些bean,這些bean中有的可能已經(jīng)被業(yè)務(wù)方系統(tǒng)配置使用了,在非SpringBoot方式集成中可能導(dǎo)致沖突。導(dǎo)致按type注入失敗(因?yàn)榇嬖趦蓚€(gè)已有的實(shí)現(xiàn))。為什么要強(qiáng)調(diào)非SpringBoot呢,因?yàn)镾pringBoot可以使用@ConditionOnMissingXxx條件注解。

如何解決非SpringBoot集成情況下可能存在沖突的問題,有以下三種方案:
1、強(qiáng)制業(yè)務(wù)系統(tǒng)集成出現(xiàn)沖突時(shí)使用@Qualifier標(biāo)明其自己已存在的沖突bean,以防止按type注入出現(xiàn)的沖突異常。而我們自己的二方庫也得加上@Qualifier,但是我們需要同時(shí)提供普通Spring集成和SpringBoot集成方式,那么加上@Qualifier又會(huì)成為我們自己給自己設(shè)置的麻煩,因?yàn)樵诖嬖?strong>@ConditionOnMissingBean的時(shí)候,我們極有可能沿用業(yè)務(wù)系統(tǒng)配置的bean,而此時(shí)我們是不知道其id的。從而導(dǎo)致@Qualifier失去了意義。

2、使用@Primary標(biāo)注我們二方庫里的bean為首選項(xiàng),這樣對(duì)于業(yè)務(wù)系統(tǒng)而言就會(huì)出現(xiàn)太過透明而無法知曉到底注入了它自己的還是二方庫里的,而且如果業(yè)務(wù)系統(tǒng)存在特殊配置呢就被我們覆蓋了。還有種方式是強(qiáng)制業(yè)務(wù)系統(tǒng)標(biāo)注自己的bean為@Primary。

3、采用動(dòng)態(tài)加載裝配bean定義的方式。如果已存在則不裝配,不存在才進(jìn)行裝配。效果類似于@ConditionMissingBean

由于第三種,對(duì)于集成方更為友好,所以采用第三種方案去實(shí)施。
主體思路:在Spring裝配完所有的bean之后才實(shí)施我們的動(dòng)態(tài)裝配邏輯。

Spring的bean生命周期中的一些鉤子

Spring提供了很多接口用于我們?cè)赽ean初始化前后實(shí)現(xiàn)自定義邏輯,目前接觸較多的有:BeanFactoryAware,ApplicationContextAware,ApplicationListener,InitializingBean,BeanPostProcessor。
針對(duì)這幾個(gè)接口,我們梳理下bean初始化執(zhí)行順序:
bean本身的構(gòu)造器初始化調(diào)用->BeanPostProcessor的前置處理調(diào)用postProcessBeforeInitialization->InitializingBean的afterPropertiesSet調(diào)用->BeanPostProcessor的后置處理調(diào)用postProcessAfterInitialization
要驗(yàn)證以上順序也不難,可以寫幾個(gè)實(shí)現(xiàn),打印一些日志,觀察前后輸出,去測(cè)試驗(yàn)證一下,簡(jiǎn)要截圖如下:

注意:其中MyBean4是BeanPostProcessor的實(shí)現(xiàn)類。

這只討論了單個(gè)bean的一個(gè)執(zhí)行順序,那么所有bean是哪個(gè)時(shí)候執(zhí)行完的呢,或者說回調(diào)那個(gè)接口方法時(shí),所有已經(jīng)初始化完成并已完全就緒呢?我們來分析下這三個(gè)接口BeanFactoryAware,ApplicationContextAware,ApplicationListener
BeanFactoryAware,ApplicationContextAware這兩個(gè)我們一般用來獲取并保持容器上下文,剛開始我在想,是不是在setBeanFactory或者setApplicationContext的時(shí)候,所有bean已經(jīng)就緒呢?

從截圖來看,很遺憾,并沒有如我們想象般那樣。
接下來我們關(guān)注一下這個(gè)ApplicationListener。Spring為我們提供了以下幾個(gè)事件以便于我們對(duì)容器生命周期的監(jiān)聽:

ContextRefreshedEvent:This event is published whenever the Spring Context is started or refreshed.

ContextStartedEvent:This event is published when the Spring Context is started.

ContextStoppedEvent:This event is published when the Spring Context is stopped. In practice you will not use this event very often. It can be handy for doing cleanup work, like closing connections.

ContextClosedEvent:This event is similar to the ContextStoppedEvent, but in this case the Context can not be re-started.

我們的關(guān)注點(diǎn)自然落在了ContextRefreshedEvent及ContextStartedEvent,但是按說服,似乎ContextRefrehedEvent更符合我們的需求,我們驗(yàn)證一下,是否該事件是在所有bean都就緒之后才會(huì)發(fā)出。

從日志中我們可以看到,在打印了onApplicationEvent日志之后,沒有再出現(xiàn)beanPostProcessor的日志,說明確實(shí)是在所有bean都初始化完成之后才調(diào)用。

上述需求的動(dòng)態(tài)裝配實(shí)現(xiàn)代碼如下:

public class SpringListener implements ApplicationListener {

    private static final Logger log=LoggerFactory.getLogger(SpringListener.class);

    private  static ApplicationContext applicationContext;


    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

        log.info("bizrule dynamic bean load start...");
        applicationContext=contextRefreshedEvent.getApplicationContext();

        XRuleEngine xRuleEngine=applicationContext.getBean(XRuleEngine.class);

        try {
            ConfigProps configProps=new ConfigProps(xRuleEngine);

    
            DynamicBeanLoader.dynamicLoadBizBean(applicationContext,EmployeeBasicService.class,
                    configProps.getMasterDataVersion(),"",Integer.valueOf(configProps.getMasterDataTimeout()+""));

            DynamicBeanLoader.dynamicLoadBizBean(applicationContext,EmployeeJobLevelService.class,
                    configProps.getMasterDataVersion(),"",Integer.valueOf(configProps.getMasterDataTimeout()+""));

            DynamicBeanLoader.dynamicLoadBizBean(applicationContext,EmployeeDeptService.class,
                    configProps.getMasterDataVersion(),"",Integer.valueOf(configProps.getMasterDataTimeout()+""));

   
            DynamicBeanLoader.dynamicLoadBean(applicationContext,MasterdataServiceImpl.class,
                Arrays.asList("secret::"+configProps.getMasterDataSecret(),"clientId::"+configProps.getMasterDataClientId())
                    .stream().map(v->v.split("::")).collect(Collectors.toMap(v->v[0],v->v[1])));

            MasterdataService masterdataService=applicationContext.getBean(MasterdataService.class);

            DynamicBeanLoader.dynamicLoadBean(applicationContext,RcUserServiceImpl.class,new HashMap());

            RcUserService rcUserService=applicationContext.getBean(RcUserService.class);

            log.info("start autowired bizrule plugin beans");
            BizAmountDecisionService bizAmountDecisionService= null;
            try {
                bizAmountDecisionService = applicationContext.getBean(BizAmountDecisionService.class);
                bizAmountDecisionService.setRcUserService(rcUserService);
                log.info("autowired beans for bizAmountDecisionService");
            } catch (NoSuchBeanDefinitionException e) {
                log.info("didn"t find BizAmountDecisionService bean, ignore it"s RcUserService autowired. ");
            }

            UserReportLineService userReportLineService= null;
            try {
                userReportLineService = applicationContext.getBean(UserReportLineService.class);
                userReportLineService.setRcUserService(rcUserService);
                log.info("autowired beans for userReportLineService");
            } catch (NoSuchBeanDefinitionException e) {
                log.info("didn"t find UserReportLineService bean, ignore it"s RcUserService autowired. ");
            }
            log.info("dynamic bizrule common bean loader over");
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage(),e);
        }
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static void setApplicationContext(ApplicationContext applicationContext) {
        SpringListener.applicationContext = applicationContext;
    }
}
public class DynamicBeanLoader {

    private static final Logger log=LoggerFactory.getLogger(DynamicBeanLoader.class);

    @Deprecated
    public static void dynamicLoadBean(ApplicationContext context,Resource... resources) {
        AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(
            registry);
        beanDefinitionReader.setResourceLoader(context);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(context));
        try {
            for (int i = 0; i < resources.length; i++) {
                beanDefinitionReader.loadBeanDefinitions(resources[i]);
            }
        } catch (BeansException e) {
            e.printStackTrace();
            log.error(e.getMessage(),e);
        }
    }
    public static void dynamicLoadBean(ApplicationContext context,Class clazz,Map params){
        if(isBeanLoaded(context,clazz)){
            return;
        }
        AutowireCapableBeanFactory autowireCapableBeanFactory = context.getAutowireCapableBeanFactory();
        DefaultListableBeanFactory beanFactory=(DefaultListableBeanFactory)autowireCapableBeanFactory;
        GenericBeanDefinition gbd = new GenericBeanDefinition();
        gbd.setBeanClass(clazz);
        MutablePropertyValues mpv = new MutablePropertyValues();
        params.entrySet().stream().forEach(e->{
            mpv.add(e.getKey(), e.getValue());
        });
        gbd.setPropertyValues(mpv);
        beanFactory.registerBeanDefinition("xc"+clazz.getSimpleName(), gbd);
    }
    public static void dynamicLoadBizBean(ApplicationContext context,Class clazz,String version,String group,int timeout){
        //略。。。。
    }

    public static boolean isBeanLoaded(ApplicationContext context,Class clazz){
        //之所以采用捕獲異常而不是containsBean的形式,是因?yàn)閏ontainsBean僅支持按名稱判斷,在這種場(chǎng)景下有局限性。
        try {
            context.getBean(clazz);
            log.info("find {} in system, ignore load",clazz.getName());
            return true;
        } catch (NoSuchBeanDefinitionException e) {
            log.info("not find {} in system, will being load",clazz.getName());
            return false;
        }
    }

}
SpringBoot Starter開發(fā)

我們同時(shí)提供SpringBoot starter集成。
先貼上官方文檔:https://docs.spring.io/spring...
官方文檔寫得很簡(jiǎn)單很抽象。不過提供了思路,SpringBoot啟動(dòng)的時(shí)候會(huì)去尋找META-INF/spring.factories文件,去加載其中的配置進(jìn)行相應(yīng)的初始化。默認(rèn)配置由SpringBoot Autoconfigure模塊提供,注意到其中的片段:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,

這里定義了自動(dòng)配置的類,我們可找到對(duì)應(yīng)的類看看實(shí)現(xiàn)。

然后我們可以隨便挑幾個(gè)starter看看,總結(jié)下思路步驟
1、定義AutoConfiguration類
2、如果有需要在application.properties加些配置,可以定義XxxProperties類
3、定義META-INF/spring.factories文件

具體照著代碼說明吧:
pom依賴添加,這里版本就不列出來了。

        
            org.springframework.boot
            spring-boot
        

        
            org.springframework.boot
            spring-boot-autoconfigure
        

XxxProperties類定義:

@ConfigurationProperties(prefix = "xrulecenter.biz")
public class AmountDesionProperties {
}

這樣我們就可以在application.properties中使用 ? xrulecenter.biz.屬性=value 來配置

AutoConfiguration類定義

@Configuration
@EnableConfigurationProperties(value = {GetAuditorProperties.class})
@ConditionalOnClass(BizAuditorServiceImpl.class)
@ConditionalOnProperty(prefix = "xrulecenter.bizrule.enabled",name={"getauditor"},havingValue = "true",matchIfMissing = true)
public class GetAuditorAutoConfiguration {

    @Autowired
    private GetAuditorProperties getAuditorProperties;


    @Bean
    @ConditionalOnMissingBean
    public SpringListener xcBizRuleSpringListener(){
        return new SpringListener();
    }

    @Bean
    @ConditionalOnMissingBean
    public BizAuditorService bizAuditorService(){
        return new BizAuditorServiceImpl();
    }
    @Bean
    @ConditionalOnMissingBean
    public UserReportLineService xcUserReportLineService(){
        return new UserReportLineServiceImpl();
    }
}

這里有幾個(gè)注解需要注意一下:
@EnableConfigurationProperties使我們前面定義的properties能生效
@ConditionalOnXxx 條件注入生效注解,其中@ConditionalOnProperty需要關(guān)注一下:通過它我們可以配置自己starter模塊的開關(guān)配置,而不是引入依賴即開啟,讓用戶有選擇地打開和關(guān)閉。
havingValue = "true",matchIfMissing = true這兩個(gè)屬性同時(shí)添加的意思是:如果用戶在application.properties配置了xrulecenter.bizrule.enabled=true或者沒有此配置都將使該starter生效。

定義spring.factories文件:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
  com.auditor.GetAuditorAutoConfiguration

這樣我們的starter就完工了。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69598.html

相關(guān)文章

  • 常識(shí)之外規(guī)范——阿里java開發(fā)手冊(cè)筆記(全章節(jié))

    摘要:說明這篇文章是我第一次認(rèn)真閱讀阿里巴巴開發(fā)手冊(cè)終極版的筆記。說明本手冊(cè)明確防止是調(diào)用者的責(zé)任。一年半載后,那么單元測(cè)試幾乎處于廢棄狀態(tài)。好的單元測(cè)試能夠最大限度地規(guī)避線上故障。 說明 這篇文章是我第一次(認(rèn)真)閱讀《阿里巴巴 Java 開發(fā)手冊(cè)(終極版)》的筆記。手冊(cè)本身對(duì)規(guī)范的講解已經(jīng)非常詳細(xì)了,如果你已經(jīng)有一定的開發(fā)經(jīng)驗(yàn)并且有良好的編碼習(xí)慣和意識(shí),會(huì)發(fā)現(xiàn)大部分規(guī)范是符合常識(shí)的。所以...

    Martin91 評(píng)論0 收藏0
  • 如何成為優(yōu)秀技術(shù)主管?你要做到這三點(diǎn)

    摘要:的報(bào)告進(jìn)一步證實(shí)了與成功項(xiàng)目最密切的因素是良好的需求管理,也就是項(xiàng)目的范圍管理,特別是管理好項(xiàng)目的變更。需求管理的第一步就是要梳理不同來源的需求,主要包括從產(chǎn)品定位出發(fā)外部用戶反饋競(jìng)爭(zhēng)對(duì)手情況市場(chǎng)變化以及內(nèi)部運(yùn)營(yíng)人員客服人員開發(fā)人員的反饋。 showImg(https://upload-images.jianshu.io/upload_images/2509688-ac38883baf...

    mist14 評(píng)論0 收藏0
  • Java 平臺(tái)微服務(wù)架構(gòu)項(xiàng)目組成形式

    摘要:微服務(wù)項(xiàng)目的依賴關(guān)系在微服務(wù)化架構(gòu)中軟件項(xiàng)目被拆分成多個(gè)自治的服務(wù)服務(wù)之間通過網(wǎng)絡(luò)協(xié)議進(jìn)行調(diào)用通常使用透明的遠(yuǎn)程調(diào)用在領(lǐng)域每個(gè)服務(wù)上線后對(duì)外輸出的接口為一個(gè)包在微服務(wù)領(lǐng)域包被分為一方庫二方庫三方庫一方庫本服務(wù)在進(jìn)程內(nèi)依賴的包二方庫在服務(wù)外通 微服務(wù)項(xiàng)目的依賴關(guān)系 在微服務(wù)化架構(gòu)中, 軟件項(xiàng)目被拆分成多個(gè)自治的服務(wù), 服務(wù)之間通過網(wǎng)絡(luò)協(xié)議進(jìn)行調(diào)用, 通常使用透明的 RPC 遠(yuǎn)程調(diào)用. 在...

    URLOS 評(píng)論0 收藏0
  • 巧妙復(fù)制一個(gè)流

    摘要:場(chǎng)景實(shí)際業(yè)務(wù)中可能出現(xiàn)重復(fù)消費(fèi)一個(gè)可讀流的情況,比如在前置過濾器解析請(qǐng)求體,拿到進(jìn)行相關(guān)權(quán)限及身份認(rèn)證認(rèn)證通過后框架或者后置過濾器再次解析請(qǐng)求體傳遞給業(yè)務(wù)上下文。 場(chǎng)景 實(shí)際業(yè)務(wù)中可能出現(xiàn)重復(fù)消費(fèi)一個(gè)可讀流的情況,比如在前置過濾器解析請(qǐng)求體,拿到body進(jìn)行相關(guān)權(quán)限及身份認(rèn)證;認(rèn)證通過后框架或者后置過濾器再次解析請(qǐng)求體傳遞給業(yè)務(wù)上下文。因此,重復(fù)消費(fèi)同一個(gè)流的需求并不奇葩,這類似于js...

    wenzi 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<