前言:实战篇demo 实现了使用socket通过tcp 连接完成客户端与服务端接口的调用和 数据返回,但是实际上在spring 中我们通常使用注解的方式完成方法的调用;
1 服务端修改:
1.1 定义注解 RpcShopRemote,并在实现类上增加改注解标识 :
package com.example.springcloudshopservice.shop;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 定义注解
@Component
@Target(ElementType.TYPE)// 定义注解范围,type代表类
@Retention(RetentionPolicy.RUNTIME)// 保留的时间
public @interface RpcShopRemote {
}
ShopService:
package com.example.springcloudshopservice.shop;
import org.example.service.ShopService;
//@Service
@RpcShopRemote
public class ShopServiceImpl implements ShopService {
@Override
public String getOneShop(String shopId) {
return "苹果";
}
}
1.2 需要在spring 容器启动之后对外发布服务:
1) 定义存放对外发布服务bean 的类:
package com.example.springcloudshopservice.shop;
import org.example.service.RpcRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Mediator {
// 放入bean 的实例
public static Map<String, BeanMethod> map = new ConcurrentHashMap<>();
private static volatile Mediator instance;
// 单例实现
public static Mediator getInstance() {
if (null == instance) {
synchronized (Mediator.class) {
if (null == instance) {
instance = new Mediator();
}
}
}
return instance;
}
// 定义具体调用实现类的方法
public Object process(RpcRequest request) {
String key = request.getClassName() + "." + request.getMethodName();
BeanMethod beanMethod = map.get(key);
if (null == beanMethod) {
return null;
}
Method method = beanMethod.getMethod();
try {
return method.invoke(beanMethod.getBean(), request.getArgs());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
2)将带有RpcShopRemote 注解的发布类统一放入到Mediator 的map :
package com.example.springcloudshopservice.shop;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
public class InitMediator implements BeanPostProcessor {
/**
* bean 加载完成之后
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 只处理加了RpcShopRemote注解的bean
if (bean.getClass().isAnnotationPresent(RpcShopRemote.class)) {
Method[] methods = bean.getClass().getDeclaredMethods();
for (Method method : methods) {
Class<?>[] classes = bean.getClass().getInterfaces();
String key = bean.getClass().getInterfaces()[0].getName() + "." + method.getName();
BeanMethod beanMethod = new BeanMethod(bean, method);
Mediator.map.put(key, beanMethod);
}
}
return bean;
}
}
- spring 容器启动完成后发布服务:
package com.example.springcloudshopservice;
import com.example.springcloudshopservice.shop.ProcessHandler;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// spring 容器启动完成之后发布 ContextRefreshedEvent 事件
@Component
public class SocketServerInitial implements ApplicationListener<ContextRefreshedEvent> {
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ServerSocket socket = null;
try{
// 服务端建立连接
socket = new ServerSocket(8080);
while (true){
Socket socket1 =socket.accept();
// 有连接进入使用线程进行方法调用
executorService.execute(new ProcessHandler(socket1));
}
}catch (Exception e){
}finally {
if (null != socket){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4) 具体方法的调用:
package com.example.springcloudshopservice.shop;
import org.example.service.RpcRequest;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.Socket;
public class ProcessHandler implements Runnable {
private Socket socket;
private Object service;
public ProcessHandler(Socket socket, Object service) {
this.socket = socket;
this.service = service;
}
public ProcessHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
ObjectInputStream inputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
inputStream = new ObjectInputStream(socket.getInputStream());
RpcRequest request = (RpcRequest) inputStream.readObject();
// 反射调用方法
// Object res = invokeMenthod(request);
Mediator mediator = Mediator.getInstance();
Object res = mediator.process(request);
System.out.println("res = " + res);
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(res);
objectOutputStream.flush();
} catch (Exception e) {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
if (null != objectOutputStream) {
try {
objectOutputStream.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
}
private Object invokeMenthod(RpcRequest request) throws Exception {
Class classz = Class.forName(request.getClassName());
Method method = classz.getMethod(request.getMethodName(), request.getTypes());
return method.invoke(service, request.getArgs());
}
}
2 客户端:
1) 定义注解声明,并在controller 中 注入改bean:
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Component
@Target(ElementType.FIELD)// 定义注解范围,FIELD代表属性
@Retention(RetentionPolicy.RUNTIME)// 保留的时间
public @interface ShopRefrence {
}
TestController:
import org.example.service.ShopService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@ShopRefrence
private ShopService shopService;
@GetMapping("test")
public String test() {
return shopService.getOneShop("123");
}
}
2) spring 容器在bean 初始化之前完成对类中增加ShopRefrence 注解的属性完成代理:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
@Component
public class ReferenceInvokeProxy implements BeanPostProcessor {
// 注入具体方法的实现
@Autowired
private RemoteInvocationHandler remoteInvocationHandler;
/**
* bean 初始化之前完成
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Field [] fields = bean.getClass().getDeclaredFields();
if (null == fields || 0== fields.length){
return bean;
}
for (Field field : fields) {
if (field.isAnnotationPresent(ShopRefrence.class)){
Object proxy= Proxy.newProxyInstance(field.getType().getClassLoader(),new Class<?>[]{
field.getType()},remoteInvocationHandler);
try {
field.setAccessible(true);
// 设置属性的代理为具体的remoteInvocationHandler实现
field.set(bean,proxy);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return bean;
}
}
3) 代理的实现RemoteInvocationHandler:
扫描二维码关注公众号,回复:
14656027 查看本文章
import org.example.service.RpcRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@Component
public class RemoteInvocationHandler implements InvocationHandler {
@Value("${rpc.shop.host:localhost}")
private String host;
@Value("${rpc.shop.port:8080}")
private int port;
// public RemoteInvocationHandler(String host, int port) {
// this.host = host;
// this.port = port;
// }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 建立远程通信连接
RpcNetTransport rpcNetTransport = new RpcNetTransport(host,port);
RpcRequest request = new RpcRequest();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setTypes(method.getParameterTypes());
request.setArgs(args);
return rpcNetTransport.send(request);
}
}
4)连接的建立以及收发消息 RpcNetTransport:
import org.example.service.RpcRequest;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class RpcNetTransport {
private String host;
private int port;
public RpcNetTransport(String host, int port) {
this.host = host;
this.port = port;
}
public Socket getSocket() throws IOException {
return new Socket(host, port);
}
public Object send(RpcRequest request){
Socket socket = null;
ObjectInputStream inputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
socket = getSocket();
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(request);
objectOutputStream.flush();
inputStream = new ObjectInputStream(socket.getInputStream());
return inputStream.readObject();
}catch (Exception ex){
ex.printStackTrace();
}finally {
try {
if (null != socket){
socket.close();
}
}catch (Exception e){
}
}
return null;
}
}
3 测试:
启动服务端和客户端后测试方法,完成方法的调用和结果的返回:
4 总结:
- 服务端在ben 完成加载后,检查所有带有对外提供服务的注解类,并将具体实现类的bean 放入到map 中;
- 服务端在spring容器启动之后,建立对特定端口的连接,如果有连接进入,则使用线程通过反射调用具体类中的方法;
- 客户端通过在bean 初始化之前,完成对需要调用远程服务的注解扫描,客户端在调用远程服务时通过代理的方式,通过建立对应服务的socket 连接,发起方法的调用;