一.动态代理的理解
动态代理是一种在运行时生成代理对象的机制,用于在不修改原始类的情况下对其进行拦截和增强。
通过动态代理,我们可以在调用目标对象的方法之前或之后插入自定义的逻辑。
动态代理通过使用一组代理类来包装目标对象,并将方法调用重定向到代理类。
代理类可以在方法调用前后执行特定的代码,实现对目标对象方法的拦截、改变参数、增强功能等操作。
Java中的动态代理主要使用
java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来实现
二.动态代理的类型
Java的动态代理主要有两种类型:基于接口的动态代理和基于类的动态代理。
1.基于接口的动态代理:使用
java.lang.reflect.Proxy
类创建代理对象2.基于类的动态代理:使用第三方库(如CGLIB或Byte Buddy)创建代理对象
二者的主要区别
这两种动态代理类型在Java中具有不同的应用场景和特点。基于接口的动态代理适用于接口代理和对特定接口的方法增强,而基于类的动态代理适用于对类的方法进行代理和增强,无需依赖接口的情况
基于接口的动态代理不需要第三方库,只需要有JDK即可,今天着重来讲解一下基于接口的动态代理
三.动态代理例子
下面一个例子解释下基于接口的动态代理
首先定义一个接口Calculator
public interface Calculator { int add(int i, int j); int sub(int i, int j); int mul(int i, int j); int div(int i, int j); }
创建一个CalculatorImpl实现类
public class CalculatorImpl implements Calculator { @Override public int add(int i, int j) { int result = i + j; System.out.println("方法内部 result = " + result); return result; } @Override public int sub(int i, int j) { int result = i - j; System.out.println("方法内部 result = " + result); return result; } @Override public int mul(int i, int j) { int result = i * j; System.out.println("方法内部 result = " + result); return result; } @Override public int div(int i, int j) { int result = i / j; System.out.println("方法内部 result = " + result); return result; } public void run(){ System.out.println("run......"); } }
创建一个获取代理对象的类
public class ProxyFactory { //目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } //返回代理对象 public Object getProxy(){ /*1.加载动态生成代理类的类加载器 *2.目标对象实现的所有接口的class类型数组 * */ //第一个参数 ClassLoader classLoader = target.getClass().getClassLoader(); //2.第二个参数 Class<?>[] interfaces = target.getClass().getInterfaces(); //3.第三个参数 InvocationHandler invocationHandler = new InvocationHandler() { //第一个参数:代理对象 //第二个参数:需要重写目标对象的方法 //第三个参数:method方法里的参数 @Override //args proxy.add(1, 2);传过来的参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("日志-动态代理-前"+method.getName()); Object result = method.invoke(target, args); System.out.println("日志-动态代理-后"+method.getName()); return result; } }; //会通过我们传入的interfaces生成代理类 return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler); } }
创建一个测试类
public class ProxyTest { @Test public void test1(){ ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl()); // Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);会产生一个代理类,是系统自动生成的 Calculator proxy = (Calculator) proxyFactory.getProxy(); //调用proxy.add(1, 2)实际会在生成的代理类中的add方法中执行,在InvocationHandler的invoke()方法,而invoke方法是我们写的增强的方法 //invoke()方法 1.完成目标方法的执行 2.完成代理逻辑的执行 int add = proxy.add(1, 2); } }
结果
日志-动态代理-前add
方法内部 result = 3
日志-动态代理-后add
由此可以看出动态代理实现的方法增强
四.动态代理的流程步骤
1.首先当调用Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);时会根据我们传入的三个参数,程序自动产生一个代理类 例:$Proxy0(在内存动态生成的类,根据传入的参数自动生成的,不需要我们编写)
其中的三个参数分别是
classLoader
(类加载器):指定用于加载动态代理类的类加载器。可以通过目标对象的getClass().getClassLoader()
方法获取其类加载器,或者使用特定的类加载器来加载代理类。该参数是一个ClassLoader
类型的对象。
interfaces
(接口数组):指定要代理的接口列表。代理对象将实现这些接口。可以传入一个接口数组,即new Class[]{Interface1.class, Interface2.class}
。这些接口定义了代理对象要实现的方法。
invocationHandler
(调用处理程序):指定用于处理代理对象方法调用的对象。该对象必须实现InvocationHandler
接口。
2.生成的代理类的具体代码
例如上面的例子
//生成的代理类会自动继承Proxy类,并且实现Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);中的interfaces接口数组中的每个接口,此处只有一个接口
public class $Proxy1 extend Proxy implements Calculator{
InvocationHandler h;
public $Proxy1(InvocationHandler h) {
this.h = h;
}
//它会实现接口中的所有方法,此处我仅以一个方法来演示
@Override
public String add(int p0,String p1) {
try {//使用了 Java 反射技术,获取了add方法的具体信息
Method md = Calculator.class.getMethod("add",new Class[]{ int.class,String.class});
//此处调用了InvocationHandler 实现类里面的invoke()方法,此处this表示此代理对象 $Proxy1,md表示add方法,new Object[] {p0, p1,}表示方法里的参数
return h.invoke(this, md,new Object[] {p0, p1,})
}catch(Exception e) {
e.printStackTrace();
}
return null;
}
}........其他的实现了接口的方法
3.前面的例子当调用proxy.add(1, 2)实际会在生成的代理类中的add方法中执行,从而调用InvocationHandler的invoke()方法,而invoke方法是我们写的增强的方法
其中//invoke()方法需要完成以下两个任务
1.完成目标方法的执行 2.完成代理逻辑的执行
最终得出结果
五.总结
1.代理类是程序运行的过程中,根据被代理的接口来动态生成代理类 $Proxy0 的 class 字节码文件
2.产生的代理类 $Proxy0 默认继承了 Proxy 类,同时实现了被代理类的接口(如 Calculator);所以才能强制将代理对象转换为被代理类的接口的类型(如 Calculator),然后可以调用 $Proxy0 中的 add() 方法
3.而在代理对象 $Proxy0 调用其实现了接口方法(如 add())时,其本质就是使用了代理对象 $Proxy0 调用了 invoke() 方法
4.而 invoke() 方法的实现是自定义的,如 CustomInvocationHandler类,在这个类中可以处理相应的代理业务逻辑
5.JDK 动态代理不能对类进行代理:因为代理类($Proxy0)已经继承了 Proxy 类,由于 Java 语言只支持单继承,所以 JDK 动态代理不能对类进行代理,只能对接口类型进行代理