Skip to content

集成 SpringBoot Starter

字数: 0 字 时长: 0 分钟

需求分析

前面我们在本地编写了一个消费者模块和一个提供者模块,已经测试 RPC 框架功能完成。但是实际使用仍然不便,需要手动调用代理类创建代理对象,而且消费者和提供者应用启动时都需要手动初始化 RPC。

我们的目标是编写一个 t-rpc-spring-boot-starter ,用户引入后只需要几个注解即可使用 RPC 框架,而无需关心 RPC 是如何运作的。

依赖引入

我们创建一个 t-rpc-spring-boot-starter 模块,引入必要依赖,其中包含我们编写的 t-rpc-core 框架

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>cn.ttdgg</groupId>
    <artifactId>t-rpc-core</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

代码实现

分别创建 3 个注解和 4 个启动类

starter1.webp

  • @EnableRpc 注解

这个注解用来标注在启动类上,表示是否启动 RPC 服务,服务提供者需要开启 needServer 表示需要启动 Netty 服务端

java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({RpcInitBootstrap.class, RpcConsumerBootstrap.class, RpcProviderBootstrap.class, RpcServerBootstrap.class})
public @interface EnableRpc {

    /**
     * 是否需要启动 server
     * @return
     */
    boolean needServer() default true;

}
  • @RocReference 注解

这个注解标注在服务引用上,表示这个服务是 RPC 远程服务的引用

java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface RpcReference {


    Class<?> interfaceClass() default void.class;
    
}
  • @RpcService 注解

这个注解标注在远程服务实现类上,表示该实现类暴露给 RPC

java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RpcService {

    /**
     * 服务接口类
     */
    Class<?> interfaceClass() default void.class;

    /**
     * 版本
     */
    String serviceVersion() default RpcConstant.DEFAULT_SERVICE_VERSION;

}
  • RpcConsumerBootstrap 启动类

消费者启动类,这个类负责解析 @RpcRefernce 注解,注入相应的代理对象

java
public class RpcConsumerBootstrap implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        Class<?> beanClass = bean.getClass();

        Field[] declaredFields = beanClass.getDeclaredFields();

        for (Field field : declaredFields) {
            RpcReference rpcReference = field.getAnnotation(RpcReference.class);
            if (rpcReference != null) {
                Class<?> interfaceClass = rpcReference.interfaceClass();
                if (interfaceClass == void.class) {
                    interfaceClass = field.getType();
                }
                field.setAccessible(true);
                Object proxyObject = ServiceProxyFactory.getProxy(interfaceClass);
                try {
                    field.set(bean, proxyObject);
                    field.setAccessible(false);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("为字段注入代理对象失败",e);
                }
            }
        }

        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
  • RpcInitBootstrap 启动类

这个类负责在 Spring 初始化时,初始化 RPC 框架配置

java
/**
 * Spring 初始化时,初始化 RPC 框架配置
 */
public class RpcInitBootstrap implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata
            , BeanDefinitionRegistry registry) {
        boolean needServer = (boolean) importingClassMetadata.getAnnotationAttributes(EnableRpc.class.getName())
                .get("needServer");
        RpcApplication.init();
        RpcApplication.getRpcConfig().setNeedServer(needServer);
    }
}
  • RpcProviderBootstrap 启动类

提供者启动类,负责解析 @RpcService 注解,注册提供者对外暴露的服务实现

java
public class RpcProviderBootstrap implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 获取最终目标类(穿透所有代理)
        Class<?> beanClass = AopProxyUtils.ultimateTargetClass(bean);
        RpcService rpcService = AnnotationUtils.findAnnotation(beanClass, RpcService.class);
        if (rpcService != null) {
            Class<?> interfaceClass = rpcService.interfaceClass();

            if (interfaceClass == void.class) {
                interfaceClass = beanClass.getInterfaces()[0];
            }
            String serviceName = interfaceClass.getName();
            System.out.println("开始注册服务: " + serviceName);
            LocalRegistry.register(serviceName, beanClass);

            final RpcConfig rpcConfig = RpcApplication.getRpcConfig();
            RegistryConfig registryConfig = rpcConfig.getRegistryConfig();
            Registry registry = RegistryFactory.getInstance(registryConfig.getRegistry());

            ServiceMetaInfo serviceMetaInfo = ServiceMetaInfo.builder()
                    .serviceName(serviceName)
                    .serviceHost(rpcConfig.getServerHost())
                    .servicePort(rpcConfig.getServerPort())
                    .build();

            try {
                registry.register(serviceMetaInfo);
            }catch (Exception e) {
                throw new RuntimeException(serviceName + "服务注册失败",e);
            }

        }

        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
  • RpcServerBootStrap 启动类

负责在 SpringBoot 启动完毕后,启动 Netty 服务端(注意这里必须等待 SpringBoot 启动完毕,否则 Netty 会阻塞主线程开始监听,导致 SpringBoot 初始化不完全)

java
public class RpcServerBootstrap implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        final RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        if (rpcConfig.isNeedServer()) {
            System.out.println("开始启动 server");
            new NettyTcpServer().doStart(rpcConfig.getServerPort());
        }else {
            System.out.println("不启动 server");
        }
    }

}