RPC远程过程调用(二) 自定义注解实现改造一的简洁开发

RPC远程过程调用(一)中实现的RPC调用orderService的接口方法还是很复杂,需要手动新建rpc代理服务端,然后才能获取到orderService; 现在通过自定义注解,实现注解简洁开发; 

BeanPostProcessor 是Spring IOC容器给我们提供的一个扩展接口:

public interface BeanPostProcessor { 

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	} 

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

一. order-api项目不做改变

1. 服务提供接口: OrderService

public interface OrderService {
    String findOrderList();

    String findOrderById();
}

2. rpc请求信息dto : RpcRequestDto

@Data
public class RpcRequestDto implements Serializable {

    /**
     * 调用类名
     */
    private String   className;
    /**
     * 方法名称
     */
    private String   methodName;

    /**
     * 请求参数
     */
    private Object[] args;
    /**
     * 参数类型
     */
    private Class[]  type;
}

3. pom依赖

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
    </dependency>

二. order-provdier项目,自定义注解,自动发布服务实现类

注意:  spring容器启动完成之后会触发ContextRefreshedEvent事件

1. rpc自定义注解, 被注解的实现类自动发布

@Retention(RetentionPolicy.RUNTIME) // 注解保留的时间, RetentionPolicy.RUNTIME表示运行时保留
@Target(ElementType.TYPE) // 注解的作用范围, ElementType.TYPE表示作用于类/接口/枚举
@Component // 表明此注解需要被spring扫描到
public @interface ZswRemoteService {
}

2. 存储发布实例方法的对象

@Data
public class BeanMethod {
    /**
     * 发布实例
     */
    private Object bean;
    /**
     * 方法
     */
    private Method method;
}

3. 存储发布的服务实例对象

public class Mediator {

    /**
     * 存储发布的服务实例: 作为服务调度的路由
     */
    public static Map<String, BeanMethod> map = new ConcurrentHashMap<>();

    private Mediator() {
    }

    private volatile static Mediator instance;

    public static Mediator getInstance() {
        if (null == instance) {
            synchronized (Mediator.class) {
                if (null == instance) {
                    instance = new Mediator();
                }
            }
        }
        return instance;
    }

    public Object processor(RpcRequestDto rpcRequestDto) throws InvocationTargetException, IllegalAccessException {
        String key = rpcRequestDto.getClassName() + "." + rpcRequestDto.getMethodName();
        BeanMethod beanMethod = map.get(key);

        if (null != beanMethod) {
            Object bean = beanMethod.getBean();
            Method method = beanMethod.getMethod();
            return method.invoke(bean, rpcRequestDto.getArgs());
        }

        return null;
    }

}

4. 初始化 Mediator

@Component // 交给spring管理
public class InitMediator implements BeanPostProcessor {

    /**
     * bean装载完成运行方法
     *
     * @param bean 装载bean: 服务实现类
     * @param beanName bean名称
     * @return 结果
     * @throws BeansException 异常
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if (bean.getClass().isAnnotationPresent(ZswRemoteService.class)) {
        System.out.println("InitMediator postProcessAfterInitialization - 初始化Mediator - bean装载完成之后运行方法");
            System.out.println("服务实现类上 加了服务发布标记: 自定义rpc注解ZswRemoteService");
            Method[] methods = bean.getClass().getDeclaredMethods();
            for (Method method : methods) {
                // 接口全名 + . + 方法名
                String key = bean.getClass().getInterfaces()[0].getName() + "." + method.getName();
                System.out.println("Mediator.map.key :" + key);

                BeanMethod beanMethod = new BeanMethod();
                beanMethod.setBean(bean);
                beanMethod.setMethod(method);

                // 需要发布的服务方法的存储
                Mediator.map.put(key, beanMethod);
            }
        }
        return bean;
    }

}

5. spring容器启动完成之后会触发ContextRefreshedEvent事件, 在这里创建服务端连接并监听客户端请求,如果有客户端连接请求就进行处理

@Component // 交给spring管理
public class SocketServerInitial implements ApplicationListener<ContextRefreshedEvent> {

    private ExecutorService executorService = Executors.newFixedThreadPool(5);

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("SocketServerInitial onApplicationEvent - spring容器启动完成之后会触发ContextRefreshedEvent事件");

        ServerSocket serverSocket = null;
        try {
            // 创建port端口的服务端
            serverSocket = new ServerSocket(1234);

            while (true) {
                // 监听客户端请求
                Socket socket = serverSocket.accept();

                System.out.println("另外一个线程处理,此监听客户端请求的线程就不用阻塞在这里了");
                executorService.execute(new ProcessorHandler(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != serverSocket) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

6. 发布操作类ProcessorHandler

public class ProcessorHandler implements Runnable {

    /**
     * socket连接, 要发布的实例服务
     */
    private Socket socket; 

    public ProcessorHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        ObjectInputStream objectInputStream = null;
        ObjectOutputStream objectOutputStream = null;
        try {
            objectInputStream = new ObjectInputStream(socket.getInputStream());

            //  反序列化读取请求数据对象dto
            RpcRequestDto requestDto = (RpcRequestDto) objectInputStream.readObject();
            System.out.println("服务端 ProcessorHandler run requestDto : " + JSON.toJSON(requestDto));

            // 通过 路由 调用服务
            Mediator mediator = Mediator.getInstance();
            Object result = mediator.processor(requestDto);

            // 通过反射调用方法:
            //            Object result = invoke(requestDto);

            System.out.println("序列化写入 响应结果 : " + JSON.toJSON(result));
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(result);
            objectOutputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != objectInputStream) {
                    objectInputStream.close();
                }
                if (null != objectOutputStream) {
                    objectOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    } 

}

7. OrderServiceImpl订单实现类

@ZswRemoteService // rpc自定义注解,自动发布此实现类
public class OrderServiceImpl implements OrderService {
    @Override
    public String findOrderList() {
        return "OrderServiceImpl findOrderList 这就是 orderList !";
    }

    @Override
    public String findOrderById() {
        return "OrderServiceImpl findOrderById 这就是 order !";
    }
}

8. 项目启动类,创建spring ioc容器,

将项目改造为springboot项目

@SpringBootApplication
public class SpringBootOrderProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootOrderProviderApplication.class, args);
    }

}

或者修改

@ComponentScan("com.zsw.example")
@Configuration
public class App {

    public static void main(String[] args) { 
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class);
        System.out.println("applicationContext: " + applicationContext);
    }
}

三. 调用方项目修改,自定义注解,实现被注解的参数通过代理类完成自动注入

1. rpc自定义注解, 被注解的参数通过代理类完成自动注入

@Retention(RetentionPolicy.RUNTIME) // 注解保留的时间, RetentionPolicy.RUNTIME表示运行时保留
@Target(ElementType.FIELD) // 注解的作用范围, ElementType.FIELD 表示作用于参数/枚举
@Component // 表明此注解需要被spring扫描到
public @interface ZswReference {
}

2. 完成参数的自动代理与注入

@Component
public class ReferenceInvokeProxy implements BeanPostProcessor {

    @Autowired
    private RemoteInvocationHandler remoteInvocationHandler;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(bean.getClass().getSimpleName() + " - bean装载完成之前运行方法");

        Field[] fields = bean.getClass().getDeclaredFields();

        for (Field field : fields) {
            // 设置可访问
            field.setAccessible(true);

            if (bean.getClass().getSimpleName().equals("RpcTestController")) {
                System.out.println("RpcTestController " + field.getName());
            }

            if (field.isAnnotationPresent(ZswReference.class)) {
                System.out.println("属性参数加了接口增强注入标记: 自定义rpc注解ZswReference");

                Object proxy = Proxy.newProxyInstance(field.getType().getClassLoader(),
                        new Class<?>[] { field.getType() }, remoteInvocationHandler);

                try {
                    // 针对加了ZswReference注解的接口, 设置一个代理, 这个代理实现的是RemoteInvocationHandler
                    field.set(bean, proxy);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }
}

3. 远程代理实现类RemoteInvocationHandler

@Component
public class RemoteInvocationHandler implements InvocationHandler {

    @Value("${rpc.host}")
    private String host;

    @Value("${rpc.port}")
    private int    port;

    public RemoteInvocationHandler() {
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        System.out.println("RemoteInvocationHandler 建立远程连接");
        System.out.println("RemoteInvocationHandler 建立远程连接 host: " + host + ", port: " + port);
        RpcNetTransPort rpcNetTransPort = new RpcNetTransPort(host, port);

        RpcRequestDto requestDto = new RpcRequestDto();
        requestDto.setArgs(args);
        requestDto.setClassName(method.getDeclaringClass().getName());
        requestDto.setMethodName(method.getName());
        requestDto.setType(method.getParameterTypes());

        return rpcNetTransPort.send(requestDto);
    }
}

4. 远程连接发送接收参数RpcNetTransPort

public class RpcNetTransPort {

    private String host;

    private int    port;

    public RpcNetTransPort(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public Object send(RpcRequestDto requestDto) {
        System.out.println("RpcNetTransPort 发送请求");
        ObjectOutputStream objectOutputStream = null;

        ObjectInputStream objectInputStream = null;
        try {
            Socket socket = new Socket(host, port);

            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());

            System.out.println("序列化写入 请求 数据对象dto");
            objectOutputStream.writeObject(requestDto);
            objectOutputStream.flush();

            System.out.println("反序列化读取 服务端响应结果");
            objectInputStream = new ObjectInputStream(socket.getInputStream());
            return objectInputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != objectOutputStream) {
                    objectOutputStream.close();
                }
                if (null != objectInputStream) {
                    objectInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return "服务端响应失败!";
    }
}

5. application.yml新增

rpc:
  host: 127.0.0.1
  port: 1234

6. rpc 自定义注解改造 测试控制类

@RestController
public class RpcTestController {
  
    @ZswReference
    OrderService orderService; 

    @RequestMapping("/findOrderById")
    public String findOrderById() {
        return orderService.findOrderById();
    }

    @RequestMapping("/findOrderList")
    public String findOrderList() {
        return orderService.findOrderList();
    } 
}

四. 测试

1. order-provider项目启动,开始监听1234端口连接

2. 请求调用项目wei启动

3.1 通过浏览器访问测试, order-provider项目日志:

3.2 通过浏览器访问测试, 服务调用方wei项目日志:

4. 浏览器结果:

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值