暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

微服务架构之服务框架Dubbo-注解配置剖析

松华说 2019-05-02
184

下面是官方提供的一个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.dubbo.demo.provider")
    @PropertySource("classpath:/spring/dubbo-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、Bean的描述信息(例如是否是抽象类、是否单例)

                      2、depends-on属性(String类型,不是Class类型)

                      3、自动装配的相关信息

                      4、init函数、destroy函数的名字(String类型)

                      5、工厂方法名、工厂类名(String类型,不是Class类型)

                      6、构造函数形参的值

                      7、被IOC容器覆盖的方法

                      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的定义是

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

                                  这里需要注意的是它实现了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="org.apache.dubbo.demo.provider.DemoServiceImpl@3a1d593e" 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));
                                                  }

                                                  注解分析暂时先告一段落,后面文章再分析服务暴露等相关内容


                                                  文章转载自松华说,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                                  评论