RPC与动态代理

标签: Java, 设计模式

保留所有版权,请引用而不是转载本文(原文地址 https://yeecode.top/blog/10/ )。

1 RPC 简介

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。 RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。 在OSI网络通信模型中,RPC跨越了传输层和应用层。

2 RPC实现

RPC的实现中一个重要的功能就是动态代理。整个过程如下:

首先,对于用户方而言:

假设,我们有一个接口如下:

public interface RemoteService {
    public String getValue(String input);
}

只有方法名称,也不知道接口在远端的实现类的名称。

然后,我们的ProxyHandler如下:

public class ProxyHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Proxy Doing.");
        System.out.println("proxy :" + proxy.getClass().toString());
        System.out.println("method :" + method.toGenericString());
        System.out.println("args :" + args);
        System.out.println("序列化后去寻找具体实现……");
        return "Proxy Finish.";
    }
}

具体使用时这样:

public void test() {
    try {
       // 生成一个代理对象实例。注意,这里我们只需要接口,不需要知道任何实现类的信息
        RemoteService remoteService =
                (RemoteService) Proxy.newProxyInstance(
                        RemoteService.class.getClassLoader(),
                        new Class[] { RemoteService.class },
                        new ProxyHandler());

        String out2 = RemoteService.getValue("AA");
        System.out.println(out2);
    } catch (Throwable ex) {
        ex.printStackTrace();
    }
}

这样,就可以实现RPC的代理了。运行结果:

Proxy Doing.
proxy :class com.sun.proxy.$Proxy10
method :public abstract java.lang.String com.cmbchina.ccd.pluto.odin.log.remoteService.RemoteService.getValue(java.lang.String)
args :[Ljava.lang.Object;@2a742aa2
序列化后去寻找具体实现……
Proxy Finish.

那具体实现RPC可如下。

4 动态代理

cglib是通过字节码,生成了目标类的子类实现动态代理的。因此,对于final的方法无法使用cglib进行动态代理。

首先引入Cglib包:

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.9</version>
</dependency>

首先,目标对象不发生任何变化,并且该类也不需要实现任何接口了:

public class User{
    public String sayHello(String name) {
        System.out.println("hello " + name);
        return "OK";
    }
}

代理类写法如下:

public class ProxyHandler<T> implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before speak");
        Object ans = methodProxy.invokeSuper(o, objects);
        System.out.println("after speak");
        return ans;
    }
}

通过代码我们也可以看出,代理类是作为目标类的子类出现的,在调用目标类方法时,实际是调用父类。

使用如下:

public class Main {
    public static void main(String[] args) {
    
        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        
        // 设置enhancer对应的实现对象(回调对象)
        enhancer.setCallback(new ProxyHandler<>());
        
        // 设置enhancer对象的父类
        enhancer.setSuperclass(User.class);
        // 创建代理对象
        User user= (User) enhancer.create();

        // 通过代理对象调用目标方法
        String ans = user.sayHello("Jack");
        System.out.println(ans);
    }
}

使用过程比较简单: 1. 生成一个代理对象 2. 告诉代理对象,它的实现在哪里 3. 告诉代理对象,它的父类是谁 4. 生成代理类的对象 5. 调用代理对象的方法

这样,对生成的代理对象进行调用时,会调用到它的回调对象,然后回调对象中根据操作可能调用目标对象(父类)的方法。

得到结果:

before speak
hello Jack
after speak
OK

本文首发于个人知乎:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。

作者书籍推荐