Java有三种代理模式:静态代理、动态代理和cglib代理。代理模式是一种设计模式 , 提供了对目标对象额外的访问方式 , 即通过代理对象访问目标对象.举个例子 , 存在一个 对象A , 但是开
Java有三种代理模式:静态代理、动态代理和cglib代理。
代理模式是一种设计模式 , 提供了对目标对象额外的访问方式 , 即通过代理对象访问目标对象.
举个例子 , 存在一个 对象A , 但是开发人员不希望程序直接访问 对象A , 而是通过访问一个中介对象B来间接访问 对象A , 以达成访问 对象A 的目的 . 此时 , 对象A 被称为 "委托类" , 对象B 被称为 "代理类" .
根据java文件字节码的创建时间来分:
在程序运行前,已经存在代理类的字节码文件,称为“静态代理”。
在程序运行前 , 不存在代理类的字节码文件 , 这种代理模式被称为 "动态代理" , 代理类的实例对象在程序运行期间由 JVM 根据反射机制动态创建。
Java静态代理
直接引用一张图片来简单说明:

静态代理的优缺点.
-
优点
可以在不改变目标类(委托类)的前提下 , 修改目标类(委托类)的功能 .
-
缺点
当接口类需要增加修改删除方式时 , 委托类和代理类都需要修改 , 不易维护 .
当需要代理多个类时 , 由于代理类要与委托类实现相同的接口 , 因此一般有两种方法 .
-
只编写一个代理类 , 每代理一个委托类就多实现一个接口 , 但这样会导致代理类过于庞大 .
-
为每个委托类都编写一个代理类 , 但这样会导致代理类数量过多.
Java动态代理
JDK的原生动态代理API
主要是通过拦截器和反射来实现的,只需要JDK环境,不需要第三方库。
核心API分为:
java.lang.reflect.Proxy
java.lang.reflect.InvocationHandle
这两个类
java.lang.reflect.Proxy 提供了四个静态方法来为一组接口动态地生成代理类并返回代理类的实例对象
InvocationHandler getInvocationHandler(Object proxy):
通过指定的代理类实例查找与它有关联的调用处理器(InvocationHandler)实例。
Class<?> getProxyClass(ClassLoader loader, Class<?>[] interfaces) :
获取关联与指定类ClassLoader和一组接口的动态代理类的类对象。
boolean isProxyClass(Class<?> cl)
判断指定类是否为一个动态代理类。
Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):
为指定ClassLoader 、一组接口、调用处理器。
java.lang.reflect.InvocationHandler
此为调用处理器的interface,定义了一个invoke()方法,用来集中处理在动态代理类对象上的方法调用。

proxy:代理类实例对象
method:被调用方法名,为 java.lang.reflect.Method 类型
args:被调用方法的参数数组
继承该接口,再实现invoke()方法来添加代理访问的逻辑。
JDK原生动态代理实例
抽象接口类 testInterface
![]()

目标类、委托类继承实现接口类
![]()

实现调用处理器 InvocationHandler的中介代理类
![]()

最后是测试实现类,真正调用创建动态代理类proxy
package Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class test {
public static void main(String args[]){
//创建目标类的实例化对象
testDelegate delegate = new testDelegate();
//获取delegate的classloader
ClassLoader classLoader = delegate.getClass().getClassLoader();
//获取delegate对象实现的接口
Class[] interfaces = delegate.getClass().getInterfaces();
//通过代理类testHandler传入delegate对象来创建一个调用处理器
InvocationHandler invocationHandler = new testHandlder(delegate);
//创建代理对象proxy
testInterface proxy = (testInterface) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
proxy.say();
}
}
运行结果:
![]()

总的来说分为三步:
1.实现 java.lang.reflect.InvocationHandler接口来创建自定义的调用处理器 (InvocationHandler)
2.为 java.lang.reflect.Proxy类指定ClassLoader,Interfaces和InvocaHandler
3.调用 java.lang.reflect.Proxy.newProxyInstance()方法,分别传入2中的三个参数
最后创建出代理对象proxy。
注意看动态代理的代理类不需要调用目标类的方法来实现对与目标类的扩展了,
而是通过实现InvocationHandler来对于目标类testDelegate的扩展,这是和静态代理不一样的
也能比静态代理更好的通用和方便。
来调试一下
首先是创建委托类的实例化对象
![]()

然后是classloader和interfaces的获取
![]()

然后传入delegate对象去获取invocationhadler
这里重写的invoke()方法用Object去修饰,就可以返回任意类型的类对象object
也是可以用class<?>泛型来修饰,不过需要用户传入对象类型,用object就修饰就可以了
![]()

接着再调用Proxy.newProxyInstance()方法生成动态代理对象
![]()

最后就是调用say方法,输出自定义的东西,然后去invoke目标类的方法
![]()

参考:https://www.guildhab.top/2020/07/java-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E5-%E8%A7%A3%E5%AF%86-ysoserial-java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E6%9C%BA%E5%88%B6/