dubbo多实现接口注册方案思路

dubbo多接口实现注册方案

背景介绍
在前后端分离情况下服务端被调用项目也分为web端app端等,但是由于项目前期接口量少,不适合做过多的项目拆分,所以才有了这一套的过渡方案,方便日后项目拆分

实现方案

dubbo默认会使用实现类的第一个接口注册成bean并集成到可远程调用接口集,所以我们要做的就是在dubbo注册bean之后集成到可远程调用接口集之前手动将该实现类的其他接口都注册成bean即可

核心方法(在后面的 DubboConfig 类里有全代码)

	@Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
        scanner.setBeanNameGenerator(new AnnotationBeanNameGenerator());
        scanner.addIncludeFilter(new AnnotationTypeFilter(DubboService.class));
        packagesToScan.stream().peek(scanner::scan).map(scanner::findCandidateComponents).flatMap(Set::stream).forEach(this::registerServiceBean);
    }

    private void registerServiceBean(BeanDefinition beanDefinition) {
        Class<?> beanClass = ClassUtils.resolveClassName(beanDefinition.getBeanClassName(), classLoader);
        Class<?>[] interfaceClasses = ClassUtils.getAllInterfacesForClass(beanClass);

        String beanName = generateServiceBeanName(interfaceClasses[0]);
        BeanDefinition bean = registry.getBeanDefinition(beanName);

        for (int i = 1; i < interfaceClasses.length; i++) {
            // ServiceBean Bean name
            registerBeanDefinition(interfaceClasses[i], bean);
        }
    }

    private String generateServiceBeanName(Class<?> interfaceClass) {
        return ServiceBeanNameBuilder.create(interfaceClass, environment).build();
    }

    private void registerBeanDefinition(Class<?> interfaceClass, BeanDefinition bean) {
        String replaceParamName = "interface";
        AbstractBeanDefinition newBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ServiceBean.class).getBeanDefinition();
        bean.getPropertyValues().forEach(newBeanDefinition.getPropertyValues()::addPropertyValue);
        newBeanDefinition.getPropertyValues().removePropertyValue(replaceParamName);
        newBeanDefinition.getPropertyValues().add(replaceParamName, interfaceClass.getName());
        registry.registerBeanDefinition(generateServiceBeanName(interfaceClass), newBeanDefinition);
    }

代码全套

  1. 创建 @EnableMyDubbo 注解用于替换 @EnableDubbo 注解
    import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig;
    import org.springframework.core.annotation.AliasFor;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    @EnableDubboConfig
    @MyDubboComponentScan
    public @interface EnableMyDubbo {
    
        @AliasFor(annotation = MyDubboComponentScan.class, attribute = "basePackages")
        String[] scanBasePackages() default {};
    
        @AliasFor(annotation = MyDubboComponentScan.class, attribute = "basePackageClasses")
        Class<?>[] scanBasePackageClasses() default {};
    
        @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
        boolean multipleConfig() default true;
    }
    
  2. 创建 @MyDubboComponentScan 注解用于替换 @DubboComponentScan 注解
    import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan;
    import org.springframework.context.annotation.Import;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @DubboComponentScan
    @Import(MyDubboComponentScanRegistrar.class)
    public @interface MyDubboComponentScan {
    
        String[] value() default {};
    
        String[] basePackages() default {};
    
        Class<?>[] basePackageClasses() default {};
    }
    
  3. 创建我们自己的dubbo扫描类 MyDubboComponentScanRegistrar
    import org.apache.dubbo.config.annotation.Service;
    import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor;
    import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor;
    import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.annotation.AnnotationAttributes;
    import org.springframework.core.type.AnnotationMetadata;
    import org.springframework.util.ClassUtils;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.LinkedHashSet;
    import java.util.Set;
    import java.util.stream.Stream;
    
    import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
    
    
    public class MyDubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
            registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
        }
    
    	private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
            AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(MyDubboComponentScan.class.getName()));
            String[] basePackages = attributes.getStringArray("basePackages");
            Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
            String[] value = attributes.getStringArray("value");
            // Appends value array attributes
            Set<String> packagesToScan = new LinkedHashSet<>(Arrays.asList(value));
            packagesToScan.addAll(Arrays.asList(basePackages));
            Stream.of(basePackageClasses).map(ClassUtils::getPackageName).forEach(packagesToScan::add);
            return packagesToScan.isEmpty() ? Collections.singleton(ClassUtils.getPackageName(metadata.getClassName())) : packagesToScan;
        }
    
        private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
            BeanDefinitionBuilder builder = rootBeanDefinition(DubboConfig.class);
            builder.addConstructorArgValue(packagesToScan);
            builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
            BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
        }
    }
    
  4. 再自定义一个多实现接口标识注解 @DubboService
    import org.apache.dubbo.config.annotation.Service;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Inherited
    @Service
    public @interface DubboService {
    
    }
    
  5. 核心,注册实现 DubboConfig
    import lombok.extern.slf4j.Slf4j;
    import org.apache.dubbo.config.spring.ServiceBean;
    import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilder;
    import org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanClassLoaderAware;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
    import org.springframework.context.EnvironmentAware;
    import org.springframework.context.ResourceLoaderAware;
    import org.springframework.context.annotation.AnnotationBeanNameGenerator;
    import org.springframework.core.env.Environment;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.core.type.filter.AnnotationTypeFilter;
    import org.springframework.util.ClassUtils;
    
    import java.util.Set;
    
    @Slf4j
    public class DubboConfig implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware {
    
        private Environment environment;
    
        private ResourceLoader resourceLoader;
    
        private ClassLoader classLoader;
    
        private BeanDefinitionRegistry registry;
    
        private Set<String> packagesToScan;
    
        public DubboConfig(Set<String> packagesToScan) {
            this.packagesToScan = packagesToScan;
        }
    
        @Override
        public void setBeanClassLoader(ClassLoader classLoader) {
            this.classLoader = classLoader;
        }
    
        @Override
        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
    
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
    
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            this.registry = registry;
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
            scanner.setBeanNameGenerator(new AnnotationBeanNameGenerator());
            scanner.addIncludeFilter(new AnnotationTypeFilter(DubboService.class));
            packagesToScan.stream().peek(scanner::scan).map(scanner::findCandidateComponents).flatMap(Set::stream).forEach(this::registerServiceBean);
        }
    
        private void registerServiceBean(BeanDefinition beanDefinition) {
            Class<?> beanClass = ClassUtils.resolveClassName(beanDefinition.getBeanClassName(), classLoader);
            Class<?>[] interfaceClasses = ClassUtils.getAllInterfacesForClass(beanClass);
    
            String beanName = generateServiceBeanName(interfaceClasses[0]);
            BeanDefinition bean = registry.getBeanDefinition(beanName);
    
            for (int i = 1; i < interfaceClasses.length; i++) {
                // ServiceBean Bean name
                registerBeanDefinition(interfaceClasses[i], bean);
            }
        }
    
        private String generateServiceBeanName(Class<?> interfaceClass) {
            return ServiceBeanNameBuilder.create(interfaceClass, environment).build();
        }
    
        private void registerBeanDefinition(Class<?> interfaceClass, BeanDefinition bean) {
            String replaceParamName = "interface";
            AbstractBeanDefinition newBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ServiceBean.class).getBeanDefinition();
            bean.getPropertyValues().forEach(newBeanDefinition.getPropertyValues()::addPropertyValue);
            newBeanDefinition.getPropertyValues().removePropertyValue(replaceParamName);
            newBeanDefinition.getPropertyValues().add(replaceParamName, interfaceClass.getName());
            registry.registerBeanDefinition(generateServiceBeanName(interfaceClass), newBeanDefinition);
        }
    }
    

使用方式

  1. 编写app端接口

    public interface AppService {
    
        String appService();
    }
    
    
  2. 编写web端接口

    public interface WebService {
    
        String webService();
    }
    
  3. 启动类上由 替换掉 @ 注解

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @EnableMyDubbo
    @SpringBootApplication
    public class DubboServiceApplication {
        public static void main(String[] args) {
            SpringApplication.run(DubboServiceApplication.class, args);
        }
    }
    
  4. 编写服务提供者

    
    @DubboService
    public class ServiceImpl implements WebService, AppService {
    
        @Override
        public String webService() {
            return "webService";
        }
    
        @Override
        public String appService() {
            return "appService";
        }
    }
    
  5. 编写app端消费者

    import org.apache.dubbo.config.annotation.Reference;
    
    public class AppController {
    
        @Reference
        private WebService webService;
    
        public String test() {
            return webService.webService();
        }
    }
    
  6. 编写web端消费者

    import org.apache.dubbo.config.annotation.Reference;
    
    public class WebController {
    
        @Reference
        private AppService appService;
    
        public String test() {
            return appService.appService();
        }
    }
    

注意点

  1. 当前使用的dubbo版本是 2.7.4
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.5</version>
        </dependency>
    

    遗憾的是并不兼容2.7.4以上版本

    2.7.4版本
    fasdf

    2.7.5版本 EnableDubbo 注解上增加了 @EnableDubboLifecycle 注解
    在这里插入图片描述

    2.7.6版本虽然和2.7.4一样,但是在 EnableDubboConfig 和 DubboComponentScan 注解里的导入配置类都增加了一行 registerCommonBeans(registry); 导致spring会报由 DubboBootstrapApplicationListener 引起的重复bean注册错误
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2.7.7版本直接计划废弃 Service 和 Reference 注解
    在这里插入图片描述

总结

  1. 直至2022年2月现在已经3.0.5版本了,所以仅供参考
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值