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();
}
}