zoukankan      html  css  js  c++  java
  • Dubbo服务发布注册

    服务发布注册的入口(@DubboComponentScan)

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(DubboComponentScanRegistrar.class)
    public @interface DubboComponentScan {
    
        String[] value() default {};
       
        String[] basePackages() default {};
    
        Class<?>[] basePackageClasses() default {};
    
    }
    

    我们看到了熟悉的东西:@Import(DubboComponentScanRegistrar.class) ,跟进去我们发现该类 实现了 ImportBeanDefinitionRegistrar 接口,该接口提供了类的注册的回调。也就是说DubboComponentScanRegistrar 最后会调用 registerBeanDefinitions 方法:

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 获取到元数据中配置的扫描路径,可以是多个,所以这里是集合
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
        // 注册指定的bean
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
         // 注册通用的bean
        // @since 2.7.6 Register the common beans
        registerCommonBeans(registry);
    }
    

    DubboComponentScanRegistrar#getPackagesToScan 这个方法中就是获取 DubboComponentScan 配置的参数,进行组装返回。

    主要关注 DubboComponentScanRegistrar#registerServiceAnnotationBeanPostProcessor 方法:

    private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
         // 构建一个rootBeanDefinition
            BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
         // 将前面组装的扫描路径作为一个属性放到 ServiceAnnotationBeanPostProcessor 中
            builder.addConstructorArgValue(packagesToScan);
            builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
           //注册该Bean,毋庸置疑,这个Bean 就是 ServiceAnnotationBeanPostProcessor
            BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
    }
    

    可以看到,ServiceAnnotationBeanPostProcessor 被标记了过时,后续可能会有点变化。我们先来看一下 ServiceAnnotationBeanPostProcessor 的类图 :

    从类图可以看出,在该Bean初始化前后,会调用好几个回调方法,其中 BeanDefinitionRegistryPostProcessor 就是Bean 注册后会调用一个 postProcessBeanDefinitionRegistry 方法,该方法在其父类中:

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
          // 注册一个监听器,这个是很关键的,等等需要去看这个类
            // @since 2.7.5
            registerBeans(registry, DubboBootstrapApplicationListener.class);
          // 获取到那个扫描路径
            Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
     
            if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
           // 进行扫描 DubboService 进行注入
                registerServiceBeans(resolvedPackagesToScan, registry);
            } else {
                if (logger.isWarnEnabled()) {
                    logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
                }
            }
    }
    

    然后我们重点看 ServiceClassPostProcessor#registerServiceBeans

    private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
        // 注册一个扫描器
        DubboClassPathBeanDefinitionScanner scanner =
            new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
        // Bean 名字解析相关
        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
    
        scanner.setBeanNameGenerator(beanNameGenerator);
        // 通过注解过滤
        // refactor @since 2.7.7
        serviceAnnotationTypes.forEach(annotationType -> {
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        });
    
        // 循环遍历我们配置的扫描路径
        for (String packageToScan : packagesToScan) {
            // 扫描
            // Registers @Service Bean first
            scanner.scan(packageToScan);
            // 拼装
            // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
    
            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
                // 遍历拼装好的 BeanDefinitionHolder
                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    //注册Bean
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }
    
                if (logger.isInfoEnabled()) {
                    logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
                                beanDefinitionHolders +
                                " } were scanned under package[" + packageToScan + "]");
                }
    
            } else {
    
                if (logger.isWarnEnabled()) {
                    logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
                                + packageToScan + "]");
                }
    
            }
    
        }
    
    }
    

     来看一下注解过滤中的serviceAnnotationTypes ,其实一目了然,DubboService 是新版的修改,避免与 Spring的 Service注解重名,org.apache.dubbo.config.annotation.Service 是兼容老版本,com.alibaba.dubbo.config.annotation.Service 也是为了兼容。

    private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
        // @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007
        DubboService.class,
        // @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service
        Service.class,
        // @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : https://github.com/apache/dubbo/issues/4330
        com.alibaba.dubbo.config.annotation.Service.class
    );
    

    ServiceClassPostProcessor#registerServiceBean

    主要功能:

    • 服务以什么协议发布
    • 服务的负载均衡策略
    • 服务的容错策略
    • 服务发布端口
    private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                     DubboClassPathBeanDefinitionScanner scanner) {
    
        // 获取到需要注册的Dubbo Service 的 bean class
        Class<?> beanClass = resolveClass(beanDefinitionHolder);
    
        // 获取都 Dubbo Service 的 注解元数据
        Annotation service = findServiceAnnotation(beanClass);
    
        /**
             * The {@link AnnotationAttributes} of @Service annotation
             * 获取到我们注解上面配置的参数信息
             */
        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
    
        // 获取该实现的接口
        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
    
        // 获取实现类 类名
        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
    
        // 该方法主要是构建了一个ServiceBean
        AbstractBeanDefinition serviceBeanDefinition =
            buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
    
        
        // ServiceBean Bean name
        //获取类名,比如这里是 ServiceBean:com.demo.api.HelloService
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
    
        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
            // 然后调用注册方法
            registry.registerBeanDefinition(beanName, serviceBeanDefinition);
    
            if (logger.isInfoEnabled()) {
                logger.info("The BeanDefinition[" + serviceBeanDefinition +
                            "] of ServiceBean has been registered with name : " + beanName);
            }
    
        } else {
    
            if (logger.isWarnEnabled()) {
                logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
                            "] of ServiceBean[ bean name : " + beanName +
                            "] was be found , Did @DubboComponentScan scan to same package in many times?");
            }
    
        }
    
    }
    

    registry.registerBeanDefinition(beanName, serviceBeanDefinition); 最终通过上述代码,将一个 dubbo中提供的ServiceBean注入到Spring IOC容器。

    ServiceBean的初始化阶段

    public ServiceBean() {
          super();
          this.service = null;
    }
    

    当ServiceBean初始化完成之后,会调用下面的方法:

    @Override
    public void afterPropertiesSet() throws Exception {
        if (StringUtils.isEmpty(getPath())) {
            if (StringUtils.isNotEmpty(beanName)
                && StringUtils.isNotEmpty(getInterface())
                && beanName.startsWith(getInterface())) {
                setPath(beanName);
            }
        }
    }
    

     源码跟到这里,我们应该知道,这里注册了一个 ServiceBean ,所以跟进这个类的构造,但是发现什么都没做,但是这个时候我们需要想起来,之前 ServiceClassPostProcessor#postProcessBeanDefinitionRegistry 方法内初始化了一个监听器 DubboBootstrapApplicationListener,我们看一下该监听器监听了什么:

    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }
    

    从这个代码可以看出,这个监听器必然执行,在 Spring 上下文刷新完毕的时候走 DubboBootstrapApplicationListener#onContextRefreshedEvent

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        dubboBootstrap.start();
    }
    

    Dubbo 的初始化入口

    • 元数据/远程配置信息的初始化
    • 拼接url()
    • 如果是dubbo协议,则启动netty server
    • 服务注册

    附上这个流程的流程图:

    参考:

    org.apache.dubbo 2.7.7 服务发布注册源码

  • 相关阅读:
    1分钟去除word文档编辑限制密码
    建行信用卡微信查询
    明目地黄丸
    发动机启停技术
    ORA-12170: TNS: 连接超时
    螃蟹放进冰箱冷冻保存前,要注意什么呢?
    螃 蟹要蒸多久
    总胆固醇偏高的注意措施及治疗方法
    codeforces 375D . Tree and Queries 启发式合并 || dfs序+莫队
    codeforces 374D. Inna and Sequence 线段树
  • 原文地址:https://www.cnblogs.com/snail-gao/p/14195324.html
Copyright © 2011-2022 走看看