Spring Dubbo開發筆記(二)——dubbo初始化

概述

Spring Dubbo 是我本身寫的一個基於spring-boot和dubbo,目的是使用Spring boot的風格來使用dubbo。(便可以瞭解Spring boot的啓動過程又能夠學習一下dubbo的框架)git

項目介紹:

github: https://github.com/Athlizo/spring-dubbo-parentgithub

碼雲: https://git.oschina.net/null_584_3382/spring-dubbo-parentspring

有興趣的朋友能夠一塊兒交流學習。app

需求:

Dubbo啓動的時候,是可使用本身的Spring來啓動dubbo服務,可是如今是須要把Dubbo啓動SpringApplicationContest的邏輯放入到Spring Boot的啓動邏輯中去。(主要是針對註解的方式)框架

經常使用的接口

按照調用的順序來介紹spring-boot

ApplicationContextInitializer

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
	void initialize(C applicationContext);
}

Spring Boot啓動的時候,會掃描classpath下的META-INF.spring.factories, 其中有一個配置名爲org.springframework.context.ApplicationContextInitializer,配置項爲一些實現了ApplicationContextInitializer接口的類的全路徑名,這些類就是Spring Boot在啓動的時候首先會進行實例化。post

ApplicationListener

同ApplicationContextInitializer加載方式同樣,META-INF.spring.factories中還有另一個配置項org.springframework.context.ApplicationListener,定義在SpringBoot啓動的時候會初始化的ApplicationListener。嚴格來講ApplicationListener在整個SpringApplicationContext啓動的時候都會觸發調用邏輯(經過各類不一樣事件觸發)學習

BeanFactoryPostProcessor

public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

從名字能夠看出,這個接口的類主要是針對BeanFactory進行一些操做,和ApplicationContextInitializer實例化的方式不同,這個接口必須在BeanFactory中存在纔會在啓動中生效,所以,ApplicationContextInitializer中常作的事情就是加入一些BeanFactoryPostProcessor 。spa

BeanPostProcessor

public interface BeanPostProcessor {
         // 先於afterPropertiesSet() 和init-method
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

若是BeanFactoryPostProcessor針對的是BeanFactory,那麼BeanPostProcessor針對就是BeanFactory中的全部bean了。分別提供了Bean初始化以前和bean初始化以後的相關處理。和BeanFactoryPostProcessor同樣,也須要在啓動中註冊到BeanFactory中才會生效,通常經過BeanFactoryPostProcessor 加入。.net

小結

從上面的分析能夠看出

  • 若是須要在SpringApplication初始化的時候須要作事情,使用ApplicationContextInitializer
  • 若是在Spring啓動的各個階段有一些定製化的處理,使用ApplicationListener
  • 若是須要對BeanFactory作一些處理,例如添加一些Bean,使用BeanFactoryPostProcessor
  • 若是須要對BeanFactory中的bean作一些處理,使用BeanPostProcessor ,(我的理解是該接口主要針對bean批量處理,不然針對特定的bean就使用InitializeBean或者Init-method完成初始化邏輯)

SpringBoot接入Dubbo

注:這裏的接入主要是對使用註解的方式。

Dubbo是中處理@Service和@Reference

關鍵類:com.alibaba.dubbo.config.spring.AnnotationBean 代碼邏輯比較簡單,這個類實現了BeanFactoryPostProcessor和BeanPostProcessor這2個接口,分別做用:

  • 在BeanFactory處理結束之後,掃描Service註解的類,加入到BeanFactory中
  • 在Bean初始化以前,對@Reference註解的set方法和屬性建立遠程調用代理類並注入
  • 在Bean初始化以後,對@Service註解的類暴露遠程調用服務(生成Exporter)

所以,SpringBoot接入dubbo的關鍵在於:在完成BeanFactoryPostProcessor調用以前,把AnnotationBean加入到BeanFactory中就能夠了

接入方式

這裏不直接使用AnnotationBean,而是另外定義一個類,新類的名字爲AnnotationBeanProcessor(爲了貼代碼方便),做用是同樣的,只是修改裏面的部分處理邏輯。

方法1. 利用ApplicationContextInitializer

代碼以下

public class DubboContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    public void initialize(ConfigurableApplicationContext applicationContext) {
        AnnotationBeanProcessor annotationBeanProcessor= new AnnotationBeanProcessor(${構造參數});
        annotationBeanProcessor.setApplicationContext(applicationContext);
        applicationContext.addBeanFactoryPostProcessor(annotationBeanProcessor);
    }
}

特色——簡單暴力

方法2 利用BeanFactoryPostProcessor

在ApplicationContextInitializer中加入其它的一個BeanFactoryPostProcessor,而後在這個BeanFactoryPostProcessor加入AnnotationBeanProcessor

public class DubboContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    public void initialize(ConfigurableApplicationContext applicationContext) {
        DubboBeanDefinitionRegistryPostProcessor dubboBeanDefinitionRegistryPostProcessor = new DubboBeanDefinitionRegistryPostProcessor();
        applicationContext.addBeanFactoryPostProcessor(dubboBeanDefinitionRegistryPostProcessor);
    }

    public class DubboBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(AnnotationBeanProcessor.class);
            beanDefinition.getConstructorArgumentValues()
                    .addGenericArgumentValue(${構造參數});
            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition("annotationBeanProcessor", beanDefinition);
        }   
    }
}

特色: 這樣作雖然感受有點繞,可是好處就是能夠在其它的一些關鍵的BeanDefinitionRegistryPostProcessor 後再執行,這樣就可使用xxxAware接口,Spring會自動幫咱們注入。能夠利用Spring提供的一些便利功能。 雖然利用ApplicationListener也能夠作到,可是不推薦

方法3 利用ImportBeanDefinitionRegistrar

@Import註解中加入ImportBeanDefinitionRegistrar的實現類,實現對bean definition 層面的開發。

public class AnnotationBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    private String BEAN_NAME = "annotationBeanProcessor";

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        List<String> basePackages = getPackagesToScan(importingClassMetadata);
        if (!registry.containsBeanDefinition(BEAN_NAME)) {
            addPostProcessor(registry, basePackages);
        }
    }
    
    // register annotationBeanProcessor.class
    private void addPostProcessor(BeanDefinitionRegistry registry, List<String> basePackages) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(AnnotationBeanProcessor.class);
        beanDefinition.getConstructorArgumentValues()
                .addGenericArgumentValue(basePackages);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
    }

    //獲取掃描的包路徑
    private List<String> getPackagesToScan(AnnotationMetadata metadata) {
        //EnableDubbo 是一個註解,用於開啓掃描dubbo的bean,而且能夠本身定義掃描basePackages
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                metadata.getAnnotationAttributes(EnableDubbo.class.getName()));
        String[] basePackages = attributes.getStringArray("basePackages");
        return Arrays.asList(basePackages);
    }
}

其中還使用到了EnableDubbo.class ,其實這個是一個註解,裏面定義了basePackages的屬性。 特色:1 經過註解使Dubbo是否生效,還能夠本身配置basePackages的掃描包路徑,而不用寫死在代碼裏。2. 很Spring Boot Style

@Reference和@Service

  • @Reference註解的set方法和屬性,Dubbo會建立ReferenceBean(代理類)而後注入進進。
  • @Service就是在bean初始化,會生成一個ServiceBean,而後exporter(監聽遠程調用請求)。

而這2個流程的理想處理方式就是在BeanPostProcessor 中,由於上面這2個處理邏輯不是針對某個特殊的bean,而是針對全部的bean,只要有@Reference或者@Service,而且知足basepackage限制就行。

dubbo現有的邏輯是分別在全部bean初始化以前進行@Reference相關流程,而在全部bean初始化以後調用@Service處理流程。(這2個流程甚至在ApplicationContext初始化成功之後再進行,而且這樣作還會帶來必定的好處)

相關文章
相關標籤/搜索