松花皮蛋的黑板報
  • 分享在京東工作的技術感悟,還有JAVA技術和業內最佳實踐,大部分都是務實的、能看懂的、可復現的

掃一掃
關注公眾號

微服務架構之服務框架Dubbo-注解配置剖析

博客首頁文章列表 松花皮蛋me 2019-05-02 16:40

下面是官方提供的一個DEMO

服務提供者

public class ApplicationProvider {
/**
 * In order to make sure multicast registry works, need to specify '-Djava.net.preferIPv4Stack=true' before
 * launch the application
 */
public static void main(String[] args) throws Exception {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
    context.start();
    System.in.read();
}



@Configuration
@EnableDubbo(scanBasePackages = "org.apache..demo.provider")
@PropertySource("classpath:/spring/-provider.properties")
static class ProviderConfiguration {
    @Bean
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("multicast://224.5.6.7:1234");
        return registryConfig;
    }
}
}

服務消費者Bean,后面會對@Reference注解進行分析

@Component("demoServiceComponent")
public class DemoServiceComponent implements DemoService {

    @Reference
    private DemoService demoService;

    @Override
    public String sayHello(String name) {
        return demoService.sayHello(name);
    }
}

很容易發現@EnableDubbo是我們的突破口

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {

    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};


 }

我們再進入到DubboComponentScan.class去探索,發現還是個注解,真正的實現是DubboComponentScanRegistrar.class,而它是實現了ImportBeanDefinitionRegistrar接口的

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(DubboComponentScanRegistrar.class)
    public @interface DubboComponentScan 

ImportBeanDefinitionRegistrar接口通常和@Configuration配合使用,在@Configuration之前已注冊的Bean,可以由ImportBeanDefinitionRegistrar接口來處理,這個接口提供了如下一個方法registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry),這個方法可以拿到@Import的這個class的Annotation Metadata,以及此時的BeanDefinitionRegistry對象,通過BeanDefinitionRegistry就可以拿到目前所有注冊的BeanDefinition,然后可以對這些BeanDefinition進行額外的修改或增強

Dubbo中ComponentScanRegistrar的registerBeanDefinitions方法

  @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //獲得需要掃描的包,[org.apache.dubbo.demo.provider]
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
        //創建ServiceAnnotationBeanPostProcessor Bean
        //然后掃描指定包下@Service注解的Bean,并在BeanDefinition的MutablePropertyValues中添加多個屬性
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
        //主要是支持@Reference注解注入
        registerReferenceAnnotationBeanPostProcessor(registry);

    }
  }

服務Bean注冊

private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
    //構造Bean定義
    BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
    builder.addConstructorArgValue(packagesToScan);
    //完全內部使用
    builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
    //向IoC容器注冊解析得到的BeanDefinition 
    BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);

}

我們知道BeanDefinitionBuilder可以讓我們動態創建一個Application Context而不需要XML,從上面的代碼可以看到,這里動態注冊了一個”ServiceAnnotationBeanPostProcessor”Bean,并且設置了構造函數的參數為”packagsToScan“

ServiceAnnotationBeanPostProcessor的定義是

ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
        ResourceLoaderAware, BeanClassLoaderAware。

BeanDefinitionRegistryPostProcessor繼承自BeanFactoryPostProcessor,是一種比較特殊的BeanFactoryPostProcessor。BeanDefinitionRegistryPostProcessor中定義的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法,可以讓我們實現自定義的注冊bean定義的邏輯

另外這里實現了多個Aware接口,說明這個ServiceAnnotationBeanPostProcess在初始化時得到了增強,注入了Environment、ResourceLoader、ClassLoader

我們繼續跟下去

private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

    //自定義掃描器ClassPathBeanDefinitionScanner
    DubboClassPathBeanDefinitionScanner scanner =
            new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

    BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

    scanner.setBeanNameGenerator(beanNameGenerator);
    scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));

    for (String packageToScan : packagesToScan) {
        scanner.scan(packageToScan);

        //BeanDefinitionHolder是BeanDefinition的封裝類,它封裝了BeanDefinition,Bean的名字和別名
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

        if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

            for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                registerServiceBean(beanDefinitionHolder, registry, scanner);
            }

        } 

    }

}

我們先看下ClassPathBeanDefinitionScanner掃描器內部的處理過程如下:

1、遍歷basePackages,根據每個basePackage找出這個包下的所有的class,找出之后封裝成Resource接口集合,這個Resource接口是Spring對資源的封裝,有FileSystemResource、ClassPathResource、UrlResource實現等

2、遍歷找到的Resource集合,通過includeFilters和excludeFilters判斷是否解析。這里的includeFilters和excludeFilters是TypeFilter接口類型的集合,是ClassPathBeanDefinitionScanner內部的屬性。TypeFilter接口是一個用于判斷類型是否滿足要求的類型過濾器。excludeFilters中只要有一個TypeFilter滿足條件,這個Resource就會被過濾。includeFilters中只要有一個TypeFilter滿足條件,這個Resource就不會被過濾

3、如果沒有被過濾,把Resource封裝成ScannedGenericBeanDefinition添加到BeanDefinition結果集中

4、返回最后的BeanDefinition結果集

按照上面的說法,Dubbo會把指定包中的@Service注解類型的Class修改Befinition后都注冊成Bean

  private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                     DubboClassPathBeanDefinitionScanner scanner) {

    Class<?> beanClass = resolveClass(beanDefinitionHolder);

    Service service = findAnnotation(beanClass, Service.class);

    Class<?> interfaceClass = resolveServiceInterfaceClass(beanClass, service);

    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

    AbstractBeanDefinition serviceBeanDefinition =
            buildServiceBeanDefinition(service, interfaceClass, annotatedServiceBeanName);

    //重新生成BeanName, ${category}:${protocol}:${serviceInterface}:${version}:${group}.
    String beanName = generateServiceBeanName(service, interfaceClass, annotatedServiceBeanName);

    registry.registerBeanDefinition(beanName, serviceBeanDefinition);

}

Dubbo真正對@Service注解增強的地方在buildServiceBeanDefinition

 private AbstractBeanDefinition buildServiceBeanDefinition(Service service, Class<?> interfaceClass,
                                                          String annotatedServiceBeanName) {

    BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);

    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

    MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();

    String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
            "interface", "interfaceName");

    propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(service, environment, ignoreAttributeNames));

    //引用
    addPropertyReference(builder, "ref",    annotatedServiceBeanName);
     //接口名
    builder.addPropertyValue("interface", interfaceClass.getName());

     //提供者ProviderConfig,<dubbo:provider dynamic="false" register="true" deprecated="false" prefix="dubbo.provider" valid="true" />
    addPropertyReference(builder, "provider", providerConfigBeanName);

    //監控MonitorConfig,<dubbo:monitor valid="false" prefix="dubbo.monitor" />
     addPropertyReference(builder, "monitor", monitorConfigBeanName);

    //應用空間ApplicationConfig,<dubbo:application name="dubbo-demo-annotation-provider" valid="true" id="dubbo-demo-annotation-provider" prefix="dubbo.application" />
     addPropertyReference(builder, "application", applicationConfigBeanName);
    //模塊ModuleConfig
      addPropertyReference(builder, "module", moduleConfigBeanName);    

     //注冊中心RegistryConfig,<dubbo:registry address="multicast://224.5.6.7:1234" zookeeperProtocol="false" valid="true" id="multicast" prefix="dubbo.registries." />
     List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);
    builder.addPropertyValue("registries", registryRuntimeBeanReferences);    

     //遠程調用ProtocolConfig
      List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);
     builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);

    return builder.getBeanDefinition();

}

先看下AbstractBeanDefinition是干嘛的,發現里面基本是對一些屬性進行set\get操作,總的來說,AbstractBeanDefinition保存的屬性包括

  1. 1、Bean的描述信息(例如是否是抽象類、是否單例)
  2. 2、depends-on屬性(String類型,不是Class類型)
  3. 3、自動裝配的相關信息
  4. 4、init函數、destroy函數的名字(String類型)
  5. 5、工廠方法名、工廠類名(String類型,不是Class類型)
  6. 6、構造函數形參的值
  7. 7、被IOC容器覆蓋的方法
  8. 8、Bean的屬性以及對應的值(在初始化后會進行填充)

一個Bean可能需要依賴其他的Bean,那么這個被依賴的Bean如何在BeanDefinition中表示呢?答案就是RuntimeBeanReference,在解析到依賴的Bean時,解析器會根據Bean的name創建一個RuntimeBeanReference對象,把這個對象放入BeanDefinition的MutablePropertyValues中。那么上面addPropertyReference和最后幾行,其實就是在處理與注冊中心bean、網關協議bean等的依賴關系

而在創建Bean時,需要將依賴解析成真正的Bean,由AbstractAutowireCapableBeanFactory在applyPropertyValues方法中通過BeanDefinitionValueResolver來實現的,BeanDefinitionValueResolver將真正的依賴Bean和referBeanName關聯起來

我們再回到Dubbo中ComponentScanRegistrar的registerBeanDefinitions主流程中分析另外一個函數

 private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {

        BeanRegistrar.registerInfrastructureBean(registry,
                ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);

    }

這里將ReferenceAnnotationBeanPostProcessor注冊成Bean,它的定義是

public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor<Reference>
        implements ApplicationContextAware, ApplicationListener 

我們知道如果在上下文中部署一個實現了ApplicationListener接口的Bean,那么每當在一個ApplicationEvent發布到 ApplicationContext時,這個Bean得到通知然后執行onApplicationEvent方法,其實這是標準的Oberver設計模式

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ServiceBeanExportedEvent) {
            onServiceBeanExportEvent((ServiceBeanExportedEvent) event);
        } else if (event instanceof ContextRefreshedEvent) {
                  onContextRefreshedEvent((ContextRefreshedEvent) event);
        }
    }

而AnnotationInjectedBeanPostProcessor的定義是

 public abstract class AnnotationInjectedBeanPostProcessor<A extends Annotation> extends
InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered,BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean    

我們知道InstantiationAwareBeanPostProcessor是容器級生命周期接口,本質是BeanPostProcessor的子接口,一般我們繼承Spring為其提供的適配器類InstantiationAwareBeanPostProcessorAdapter來使用它,此接口可以在Bean實例化前、Bean實例化后分別進行操作,也可以對Bean實例化之后進行屬性操作,Dubbo正是通過這里進行@Reference的依賴注入的,原理和@Autowired差不多,這里就不展開說明了,感興趣的朋友可以網上了解。但是到這里還沒有涉及遠程調用,繼續跟著我死啃源碼吧

  @Override
    public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

        //收集元數據,包含一個Class和InjectedElement集合
        //InjectedElement集合包含一個AutowiredFieldElement和一個AutowiredMethodElement
        InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
        try {
            //通過反射進行注入              
            metadata.inject(bean, beanName, pvs);
        } catch (BeanCreationException ex) {
            throw ex;
        } catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getName()
                    + " dependencies is failed", ex);
        }
        return pvs;
    }

ReferenceBean的定義是

ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean

這里需要注意的是它實現了FactoryBean和InitializingBean。InitializingBean接口為Bean提供了屬性初始化后的處理方法,它只包括afterPropertiesSet方法,凡是繼承該接口的類,在Bean的屬性初始化后都會執行該方法

我們再看下ServiceBean的實現

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
    ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,
    ApplicationEventPublisherAware {

   @Override
    public void onApplicationEvent(ContextRefreshedEvent event) { 
            export();
         }
     }

在AnnotationConfigApplicationContext流程中,最后的finishRefresh方法會完成刷新過程,通知生命周期處理器lifecycleProcessor刷新過程,同時發出 ContextRefreshEvent通知別人。ServiceBean監聽了ContextRefreshedEvent,然后(延遲)暴露服務完成后,會發布ServiceBeanExportedEvent事件,ReferenceAnnotationBeanPostProcessor監聽該事件

接下來看下onServiceBeanExportEvent方法的處理

  //serviceBean <dubbo:service beanName="providers:dubbo:org.apache.dubbo.demo.DemoService" path="org.apache.dubbo.demo.DemoService" ref="[email protected]" generic="false" interface="org.apache.dubbo.demo.DemoService" exported="true" unexported="false" prefix="dubbo.service.org.apache.dubbo.demo.DemoService" register="true" deprecated="false" dynamic="false" id="org.apache.dubbo.demo.DemoService" valid="true" />

  private void initReferenceBeanInvocationHandler(ServiceBean serviceBean) {

         //providers:dubbo:org.apache.dubbo.demo.DemoService
        String serviceBeanName = serviceBean.getBeanName();
        //本地緩存清理
        ReferenceBeanInvocationHandler handler = localReferenceBeanInvocationHandlerCache.remove(serviceBeanName);
        //初始化
        if (handler != null) {
            handler.init();
        }
    }

InvocationHandler接口是proxy代理實例的調用處理程序實現的一個接口,每一個proxy代理實例都有一個關聯的調用處理程序。在代理實例調用方法時,方法調用被編碼分派到調用處理程序的invoke方法返回method.invoke(bean, args)

我們再回頭看下@Reference注解的ReferenceAnnotationBeanPostProcessor#doGetInjectedBean方法

   @Override
    protected Object doGetInjectedBean(Reference reference, Object bean, String beanName, Class<?> injectedType,
                                       InjectionMetadata.InjectedElement injectedElement) throws Exception {
         //名稱,consumers:dubbo:org.apache.dubbo.demo.DemoService
        String referencedBeanName = buildReferencedBeanName(reference, injectedType);

         //對象, <dubbo:reference singleton="true" interface="org.apache.dubbo.demo.DemoService" prefix="dubbo.reference.org.apache.dubbo.demo.DemoService" lazy="false" generic="false" sticky="false" id="org.apache.dubbo.demo.DemoService" valid="true" />     
        ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referencedBeanName, reference, injectedType, getClassLoader());
         //緩存
        cacheInjectedReferenceBean(referenceBean, injectedElement);
         //創建代理
        Object proxy = buildProxy(referencedBeanName, referenceBean, injectedType); 
        return proxy;
    }

buildReferenceBeanIfAbsent

 private ReferenceBean buildReferenceBeanIfAbsent(String referencedBeanName, Reference reference,
                                                     Class<?> referencedType, ClassLoader classLoader)
            throws Exception {

        ReferenceBean<?> referenceBean = referenceBeanCache.get(referencedBeanName);

        if (referenceBean == null) {
            ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
                    .create(reference, classLoader, applicationContext)
                    .interfaceClass(referencedType);
            referenceBean = beanBuilder.build();
            referenceBeanCache.put(referencedBeanName, referenceBean);
        }

        return referenceBean;
    }    

我們重點看下AbstractAnnotationConfigBeanBuilder#build方法,ReferenceBeanBuilder則是實現了這些抽象方法

   public final B build() throws Exception {

        //檢查依賴
        checkDependencies();

        // return new ReferenceBean<Object>();
        B bean = doBuild();

        configureBean(bean);

        return bean;

    }

     protected abstract B doBuild();

    protected void configureBean(B bean) throws Exception {
         //前置配置
        preConfigureBean(annotation, bean);
         //配置屬性,ReferenceBean.setRegistries(registryConfigs);
        configureRegistryConfigs(bean);
         //配置屬性
        configureMonitorConfig(bean);
         //配置屬性
        configureApplicationConfig(bean);
         //配置屬性
        configureModuleConfig(bean);
         //后置配置
        postConfigureBean(annotation, bean);

    }

在ReferenceBeanBuilder#preConfigureBean方法里主要通過DataBinder利用BeanWrapper給對象屬性設值,在設值的時候同時做Validation。屬性包括filter、listener、parameters,其中parameters屬性設置時利用了PropertyEditorSupport編輯器,將String切割后轉成Map類型

在ReferenceBeanBuilder#postConfigureBean方法中主要配置上下文、接口( ClassUtils.resolveClassName(interfaceClassName, classLoader))、消費者、方法,執行后置屬性初始化,

buildProxy

private Object buildProxy(String referencedBeanName, ReferenceBean referenceBean, Class<?> injectedType) {
    InvocationHandler handler = buildInvocationHandler(referencedBeanName, referenceBean);
    Object proxy = Proxy.newProxyInstance(getClassLoader(), new Class[]{injectedType}, handler);
    return proxy;
}

buildInvocationHandler

private InvocationHandler buildInvocationHandler(String referencedBeanName, ReferenceBean referenceBean) {

    ReferenceBeanInvocationHandler handler = localReferenceBeanInvocationHandlerCache.get(referencedBeanName);

    if (handler == null) {
        handler = new ReferenceBeanInvocationHandler(referenceBean);
    }

        //等到本地的@Service暴露后,再進行初始化
    if (applicationContext.containsBean(referencedBeanName)) { 
         localReferenceBeanInvocationHandlerCache.put(referencedBeanName, handler);
    } else {
        //立即初始化遠程的@Service對象       
        handler.init();
    }

    return handler;
} 

handler.init其實是referenceBean#get->referenceConfig#init

 private void init() {
        checkStubAndLocal(interfaceClass);
        checkMock(interfaceClass);
        ref = createProxy(map);
        String serviceKey = URL.buildKey(interfaceName, group, version);
        ApplicationModel.initConsumerModel(serviceKey, buildConsumerModel(serviceKey, attributes));
    }

注解分析暫時先告一段落,后面文章再分析服務暴露等相關內容

黑龙江6+1开奖结果查询