• 从零实现RPC框架之 7: 基于注解的服务自动注册


    1.如何让Spring扫描到服务?

    如果我们想要在服务启动的时候就将我们的服务注册好应该怎么办呢?

    ImportBeanDefinitionRegistrar接口是也是spring的扩展点之一,它可以支持我们自己写的代码封装成BeanDefinition对象;实现此接口的类会回调postProcessBeanDefinitionRegistry方法.

    spring官方就是用这种方式,实现@Component@Service等注解的动态注入机制。

    然后我们还想要通过实现一些Aware接口拿到一些实现类。

    在这里我们需要的是拿到ResourceLoader去加载,于是还可以再扫描器里面实现ResourceLoaderAware

    最后我们需要标注一下哪些使我们需要的rpc服务,因此定义一个注解@RpcService

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Inherited
    public @interface RpcService {
    
        /**
         * Service version, default value is empty string
         */
        String version() default "";
    
        /**
         * Service group, default value is empty string
         */
        String group() default "";
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    version和group是为了防止一个接口有多个实现类的时候做区分

    @Slf4j
    public class CustomScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
        private static final String SPRING_BEAN_BASE_PACKAGE = "com.xsj";
        private static final String BASE_PACKAGE_ATTRIBUTE_NAME = "basePackage";
        private ResourceLoader resourceLoader;
    
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
            //get the attributes and values ​​of RpcScan annotation
            AnnotationAttributes rpcScanAnnotationAttributes = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(RpcScan.class.getName()));
            String[] rpcScanBasePackages = new String[0];
            if (rpcScanAnnotationAttributes != null) {
                // get the value of the basePackage property
                rpcScanBasePackages = rpcScanAnnotationAttributes.getStringArray(BASE_PACKAGE_ATTRIBUTE_NAME);
            }
            if (rpcScanBasePackages.length == 0) {
                rpcScanBasePackages = new String[]{((StandardAnnotationMetadata) annotationMetadata).getIntrospectedClass().getPackage().getName()};
            }
            // Scan the RpcService annotation
            CustomScanner rpcServiceScanner = new CustomScanner(beanDefinitionRegistry, RpcService.class);
            // Scan the Component annotation
            CustomScanner springBeanScanner = new CustomScanner(beanDefinitionRegistry, Component.class);
            if (resourceLoader != null) {
                rpcServiceScanner.setResourceLoader(resourceLoader);
                springBeanScanner.setResourceLoader(resourceLoader);
            }
            int springBeanAmount = springBeanScanner.scan(SPRING_BEAN_BASE_PACKAGE);
            log.info("springBeanScanner扫描的数量 [{}]", springBeanAmount);
            int rpcServiceCount = rpcServiceScanner.scan(rpcScanBasePackages);
            log.info("rpcServiceScanner扫描的数量 [{}]", rpcServiceCount);
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    在这里插入图片描述

    这样就将自定义的bean成功转化为了beanDefination.

    2.何时暴露服务呢?

    在上面的一步中已经将服务成功注册到Spring容器中了。

    那么就好办了Spring有一个非常好用的接口BeanPostProcessor

    我们可以在实例化填充属性的时候使用bean后置处理器进行加工,也就是注册到注册中心上。

    对于bean中有@RpcService注解的类,向服务中心注册类

    @Slf4j
    @Component
    public class SpringBeanPostProcessor implements BeanPostProcessor {
    
        private final ServiceProvider serviceProvider;
        private final RpcRequestTransport rpcClient;
    //    private final AsyncNettyRpcClient asyncNettyRpcClient;
        public SpringBeanPostProcessor() {
            this.serviceProvider = SingletonFactory.getInstance(ZkServiceProviderImpl.class);
            this.rpcClient = ExtensionLoader.getExtensionLoader(RpcRequestTransport.class).getExtension("netty");
        }
    
        @SneakyThrows
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (bean.getClass().isAnnotationPresent(RpcService.class)) {
                log.info("[{}] is annotated with  [{}]", bean.getClass().getName(), RpcService.class.getCanonicalName());
                // get RpcService annotation
                RpcService rpcService = bean.getClass().getAnnotation(RpcService.class);
                // build RpcServiceProperties
                RpcServiceConfig rpcServiceConfig = RpcServiceConfig.builder()
                        .group(rpcService.group())
                        .version(rpcService.version())
                        .service(bean).build();
                serviceProvider.publishService(rpcServiceConfig);
            }
            return bean;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    最后展示一下手动注册和自动注册的使用

    @RpcScan(basePackage = {"com.xsj"})
    public class NettyServerMain {
        public static void main(String[] args) {
            // Register service via annotation
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(NettyServerMain.class);
            NettyRpcServer nettyRpcServer = (NettyRpcServer) applicationContext.getBean("nettyRpcServer");
            // Register service manually 手动注册RpcService2
            HelloService helloService2 = new HelloServiceImpl2();
            RpcServiceConfig rpcServiceConfig = RpcServiceConfig.builder()
                    .group("test2").version("version2").service(helloService2).build();
            nettyRpcServer.registerService(rpcServiceConfig);
            nettyRpcServer.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    C# OCR服务测试程序
    【pen200-lab】10.11.1.5
    金仓数据库KingbaseES安全指南--6.8. SSPI身份验证
    【多传感器融合定位】【学习汇总】
    python图片预标注
    从“新零售”到“即时零售”看中国电商之变
    任务及任务切换
    初识Kafka构造组成
    SpringBoot 使用 Sa-Token 完成权限认证
    学习ASP.NET Core Blazor编程系列三十——JWT登录(4)
  • 原文地址:https://blog.csdn.net/xsjzn/article/details/126866193