成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

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

HelKyle / 3110人閱讀

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

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

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

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

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

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

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

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

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

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

從截圖來看,很遺憾,并沒有如我們想象般那樣。
接下來我們關(guān)注一下這個ApplicationListener。Spring為我們提供了以下幾個事件以便于我們對容器生命周期的監(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)注點自然落在了ContextRefreshedEvent及ContextStartedEvent,但是按說服,似乎ContextRefrehedEvent更符合我們的需求,我們驗證一下,是否該事件是在所有bean都就緒之后才會發(fā)出。

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

上述需求的動態(tài)裝配實現(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的形式,是因為containsBean僅支持按名稱判斷,在這種場景下有局限性。
        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ā)

我們同時提供SpringBoot starter集成。
先貼上官方文檔:https://docs.spring.io/spring...
官方文檔寫得很簡單很抽象。不過提供了思路,SpringBoot啟動的時候會去尋找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,

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

然后我們可以隨便挑幾個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();
    }
}

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

定義spring.factories文件:

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

這樣我們的starter就完工了。

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

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/69598.html

相關(guān)文章

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

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

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

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

    mist14 評論0 收藏0
  • Java 平臺微服務(wù)架構(gòu)項目組成形式

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

    URLOS 評論0 收藏0
  • 巧妙復(fù)制一個流

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

    wenzi 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<