前言
在实现一个自己的RPC框架之前,需要先了解动态代理和反射,反射可以参考这篇文章 java反射。动态代理和反射可以说是RPC的精髓所在,正是通过它们,才能使远程调用“本地化”。代理模式是基本的设计模式之一,其中动态代理更为重要。代理模式的目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
静态代理模式
1、基本设计思想:一个静态的代理模式包括几个部分:抽象类、委托类,代理类,其中委托类和代理类都需要实现抽象类(比如下面的HelloService类)的接口。在main函数里面,我们调用的是代理类的方法。
使用静态代理模式有以下优点:
- 1、代理对象存在的价值主要用于拦截对真实业务对象的访问;
- 2、代理对象具有和目标对象(真实业务对象)实现共同的接口或继承于同一个类;
- 3、代理对象是对目标对象的增强,以便对消息进行预处理和后处理。
2、一个实现的demo
抽象类HelloService
1 | public interface HelloService { |
委托类HelloServiceImpl
1 | public class HelloServiceImpl implements HelloService{ |
代理类HelloServiceProxy
1 | public class HelloServiceProxy implements HelloService { |
主函数
1 | public static void main(String[] args) { |
通过调用代理类的方法,相当于对委托类进行了一个前后的消息预处理,理解起来也比较容易。但是静态代理模式有一个很大的弊端,我们会发现有一个接口需要被代理,就需要有一个代理类,十分麻烦,这时候就需要动态代理的出现。
动态代理模式
动态代理模式主要有两种方式,分别是:JDK动态代理和CGlib动态代理,这边由于我后面RPC的实现是用JDK代理的,先来介绍一下。
JDK动态代理
JDK代理通过实现InvocationHandler接口,重写invoke方法得以实现。通过该方法实现对委托类的代理的访问,是代理类完整逻辑的集中体现,包括要切入的增强逻辑和进行反射执行的真实业务逻辑。其中抽象类和委托类和之前相同。
实现InvocationHandler接口,并重写invoke方法
1 | public class MyInvocationHandler implements InvocationHandler { |
main函数实现
1 | public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { |
在调用proxy的hello方法的时候实际上调用的是代理类的方法,也就是重写的invoke,其中invoke中的method这一参数就是委托类中的hello方法。
CGLIB动态代理
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
首先是一个目标类,它并不需要实现接口
1 | public class Dog{ |
接着需要通过实现MethodInterceptor接口,并重写intercept方法来实现代理类
1 | public class MyMethodInterceptor implements MethodInterceptor{ |
测试类
1 | public class CgLibProxy { |
最后再来简单总结一下CGLib的具体实现步骤:
1.首先实例化出一个Enhance对象,并设置所需的参数,然后再enhancer.create() 创建出代理对象。
2.调用代理对象的方法,比如说有一个eat()方法,这样会进入到方法拦截器的intercept()方法中,在这个方法中会调用一个proxy.invokeSuper()方法。
3.invokeSuper中是通过FastClass机制来调用目标类的方法。FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法
最后我们总结一下JDK动态代理和Gglib动态代理的区别:
1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。