Java动态代理的应用:手写简易RPC框架
最近学习了Java动态代理相关知识,简单记录下。
一.动态代理原理
JDK动态代理是基于Java的反射机制实现的,主要涉及到java.lang.reflect包中的Proxy和InvocationHandler。动态代理与静态代理不同,在程序刚开始运行时并不存在代理类的.class文件,代理对象是在程序运行过程中动态生成的,用完即销毁。
动态代理引入了InvocationHandler(调用处理器),代理对象调用的每个方法都会通过InvocationHandler去调用具体对象中的方法。InvocationHandler是一个接口,通过实现这个接口可以定义一个横切的逻辑。然后通过反射机制调用目标类的方法,这样就能动态的把非业务逻辑和业务逻辑动态的拼接在一起,aop的原理就是动态代理。
一个实例:
假设现在一个车站的售票服务有两种,查询车票与购票,分别设置接口并实现两种服务。然后通过动态代理对象调用这两种服务。
查询车票:
public interface InquireTicket {
void inquireTicket();
}
购票:
public interface SellTicket {
void sellTicket();
}
车站服务实现上面两个接口:
public class StationTicketService implements SellTicket ,InquireTicket {
@Override
public void sellTicket() {
System.out.println("卖出车票");
}
@Override
public void inquireTicket() {
System.out.println("查询车票");
}
}
实现调用处理器:
public class InvocationHandlerImpl implements InvocationHandler {
private StationTicketService ts;
public InvocationHandlerImpl(StationTicketService ts) {
this.ts = ts;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("正准备执行"+method.getName()+"方法。。。");
method.invoke(ts,null); //通过反射调用具体方法,前后可穿插其他额外处理操作。
System.out.println(method.getName()+"执行完毕!");
return null;
}
}
编写测试类:
public class Test {
public static void main(String[] args) {
/*
* jdk动态代理
* 1.获取需要被代理对象的ClassLoader
* 2.获取需要被代理对象实现的接口列表
* 3.设置代理调用的请求处理器InvocationHandler
*/
StationTicketService ts = new StationTicketService();
ClassLoader classLoader = ts.getClass().getClassLoader();
Class[] interfaces = ts.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandlerImpl(ts);
/*
* 4.根据123点给出的信息创建代理对象
* */
Object o = Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
InquireTicket service1 = (InquireTicket)o;
service1.inquireTicket();
SellTicket service2 = (SellTicket)o;
service2.sellTicket();
}
}
运行结果:
二.手写简易RPC框架
RPC原理简图:
要实现调用远程服务能够像访问本地系统资源一样,就要让网络通信细节对使用者透明,我们需要对通信细节进行封装,可以通过动态代理实现。
实例如下:
假设现在有一个购买书籍的服务,需要远程客户端调用这个服务。
服务端购书接口:
public interface IBook {
String sellBook(String bookname);
}
服务端购买书籍实现类:
public class IBookImpl implements IBook {
@Override
public String sellBook(String bookname) {
String result = "成功购买: " + bookname;
System.out.println(result);
return result;
}
}
服务端代理(负责接收来自客户端的调用信息,处理调用请求并将结果返回客户端):
public class RpcProxyServer {
private IBook iBook = new IBookImpl() ;
public void serveStub(int port){
try {
/*
* 1.首先构建serverSocket服务监听来自客户端的请求
* */
ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
/*
* 2.接受客户端请求的方法名与参数
* */
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
String method = ois.readUTF();
Object[] objects = (Object[])ois.readObject();
Class<?>[] params = new Class[objects.length];
for (int i = 0; i < params.length; i++) {
params[i] = objects[i].getClass();
}
/*
* 3.根据请求的数据利用反射调用相应的服务
* */
Method m = IBookImpl.class.getMethod(method,params);
Object obj = m.invoke(iBook,objects);
/*
* 4.输出服务器响应数据
* */
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(obj);
objectOutputStream.flush();//强制将输出流缓冲区中的数据输出
} catch (IOException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
客户端代理(返回一个代理对象供客户端调用,提供调用接口):
public class RpcProxyClient {
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
public Object proxyClient(Class clazz){
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),myInvocationHandler);
}
}
InvocationHandler(封装通信细节):
注意:这里的InvocationHandler中的invoke方法并没有调用目标类中的方法,而是负责将需要调用的方法名称和参数发送到服务端以及接收服务端处理完的结果,具体调用目标类的方法发生在服务端。
public class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 1.构建socket连接远程服务
* */
Socket socket = new Socket("localhost",8000);
/*
* 2.想服务端发送数据
* */
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeUTF(method.getName());
oos.writeObject(args);
oos.flush();
/*
* 3.接受远程发送的数据
* */
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
return ois.readObject();
}
}
服务端发布服务:
public class RpcServer {
public static void main(String[] args) {
RpcProxyServer rpcProxyServer = new RpcProxyServer();
rpcProxyServer.serveStub(8000);
}
}
客户端调用:
public class RpcClient {
public static void main(String[] args) {
RpcProxyClient rpcProxyClient = new RpcProxyClient();
IBook o = (IBook)rpcProxyClient.proxyClient(IBookImpl.class);
System.out.println(o.sellBook("手写RPC框架"));
}
}
运行结果:
完成!