代理模式
一种设计模式
简单地说,在代理模式中存在三个角色
用户
代理
被代理的对象
用户调用代理,代理去调用被代理的对象
以此来实现功能的增强
动态代理在java中有两种实现方法
JDK中的Proxy类
CGLIB
JDK中的Proxy类步骤
实现InvocationHandler接口,创建自己的调用处理器
通过为Proxy类指定ClassLoader和一组Interface来创建动态代理类
- 被代理对象的ClassLoader和Interface
通过反射机制获取动态代理类的构造函数
- 其需要的唯一参数类型是InvocationHandler
通过构造函数创建动态代理实例
- 构造时将之前实现的InvocationHandler对象作为参数传入
这四步之后,我们就可以用使用被代理对象的方式,来使用动态代理实例了
另外
后三步可以自己手动调用Proxy类的方法来分别实现
也可以直接调用Proxy封装好的方法来一步实现
Proxy.newProxyInstance(ClassLoader, Interface[], InvocationHandler)
Demo
package cn.andl;import cn.andl.util.Computer;import cn.andl.util.impl.ComputerImpl;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 测试JDK动态代理方式 * @author Andl * @since 2023/5/29 11:17 */public class TestProxy { public static void main(String[] args) { // 创建被代理对象实例 ComputerImpl computer = new ComputerImpl(); // 实现一个调用处理器 InvocationHandler invocationHandler = new InvocationHandler() { /** * 在之后的代理类调用方法时,会实际调用这个方法 * * @param proxy 代理 * * @param method 要被代理的方法 * * @param args 方法里的参数列表 * * @return 方法的返回值 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.format("[%s] 执行 %s 方法, 参数1:%d, 参数2:%d\n", this.getClass().getName(), method.getName(), (Integer)args[0], (Integer)args[1]); // 调用被代理对象的方法,并获取返回值 Object result = method.invoke(computer, args); System.out.format("[%s] 执行 %s 方法完毕, 结果:%d\n", this.getClass().getName(), method.getName(), (Integer)result); return result; } }; //获取代理对象 Computer computerProxy = (Computer) Proxy.newProxyInstance( // 被代理对象的类加载器 computer.getClass().getClassLoader(), // 被代理对象实现的接口 computer.getClass().getInterfaces(), // 调用处理器 invocationHandler); // 执行方法 computerProxy.add(1, 2); }}
Computer接口
package cn.andl.util;/** * 计算接口 * @author Andl * @create 2023/5/29 11:18 */public interface Computer { /** * 计算a和b的和 * @param a 加数1 * @param b 加数2 * @return 和 */ int add(int a, int b);}
ComputerImpl类
package cn.andl.util.impl;import cn.andl.util.Computer;/** * 计算接口实现类 * @author Andl * @since 2023/5/29 11:23 */public class ComputerImpl implements Computer { @Override public int add(int a, int b) { System.out.format("[%s] 方法执行中\n", this.getClass().getName()); return a + b; }}
输出结果
[cn.andl.TestProxy$1] 执行 add 方法, 参数1:1, 参数2:2[cn.andl.util.impl.ComputerImpl] 方法执行中[cn.andl.TestProxy$1] 执行 add 方法完毕, 结果:3:3
Demo2
简单封装一下
package cn.andl;import cn.andl.util.Computer;import cn.andl.util.impl.ComputerImpl;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 测试JDK动态代理2 * @author Andl * @since 2023/5/29 13:19 */public class TestProxy2 { static class InvocationHandlerImpl implements InvocationHandler { Object originalObject; public Object bind(Object originalObject) { this.originalObject = originalObject; return Proxy.newProxyInstance( originalObject.getClass().getClassLoader(), originalObject.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.format("[%s] 执行 %s 方法, 参数1:%d, 参数2:%d\n", this.getClass().getName(), method.getName(), (Integer)args[0], (Integer)args[1]); Object result = method.invoke(originalObject, args); System.out.format("[%s] 执行 %s 方法完毕, 结果:%d\n", this.getClass().getName(), method.getName(), (Integer)result); return result; } } public static void main(String[] args) { // 获取代理 Computer computer = (Computer) new InvocationHandlerImpl().bind(new ComputerImpl()); // 执行方法 computer.add(1, 2); }}
输出结果
[cn.andl.TestProxy2$InvocationHandlerImpl] 执行 add 方法, 参数1:1, 参数2:2[cn.andl.util.impl.ComputerImpl] 方法执行中[cn.andl.TestProxy2$InvocationHandlerImpl] 执行 add 方法完毕, 结果:3
原理简述
通过在main方法中最开始时加入一句代码,我们可以保留动态代理对象的字节码文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
可以在和src同级的com文件夹下的
sun/proxy/
中找到
类名
public final class $Proxy0 extends Proxy implements Computer {
观察类名可以发现,动态代理类继承了Proxy方法,实现了Computer接口
静态代码块
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("cn.andl.util.Computer").getMethod("add", Integer.TYPE, Integer.TYPE); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } }
观察静态代码块可以发现,被代理对象的方法被赋值到了变量中
add方法
public final int add(int var1, int var2) throws { try { return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } }
观察动态代理类中的add方法可以发现
是通过调用父类中变量
h
的invoke方法来实现功能的而这个
h
,就是我们在之前创建动态代理类时,向构造器传入的InvocationHandler
CGLIB
cglib是一个功能强大、高性能、高质量的字节码操作库
主要用于在运行时拓展Java类或者根据接口生成对象
本身的实现基于asm库
要使用cglib主要会用到Enhancer和回调类
Enhancer
Enhancer是cglib中使用最多的类
Enhancer可以生成被代理类的子类,并且会拦截所有方法的调用
- 称之为增强
Enhancer可以基于接口来生成动态代理类,也可以直接基于类生成动态代理类
Enhancer不能增强构造函数,也不能增强被final修饰的类,或者被static和final修饰的方法
- 因为Enhancer是通过继承被代理的目标类来是实现增强的
Enhancer的使用分成两步
传入目标类型
设置回调
MethodInterceptor
cglib中回调类型有很多,这里主要介绍方法拦截器MethodInterceptor
方法拦截器会对被代理的目标类中所有可以增强的方法进行增强
- 不包括构造方法、final方法和static方法
方法拦截器的核心方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
o
- 被代理的目标对象
method
- 被代理的目标方法
objects
- 参数列表
methodProxy
- 代理类的方法引用
Demo
public class TestCGLIB { public static void main(String[] args) { // 初始化enhancer对象 Enhancer enhancer = new Enhancer(); // 传入目标类型 enhancer.setSuperclass(ComputerImpl.class); // 也可以传入接口// enhancer.setInterfaces(ComputerImpl.class.getInterfaces()); // 设置回调类型 enhancer.setCallback(new MethodInterceptor() { /** * 拦截方法 * @param o 目标对象 * @param method 目标方法 * @param objects 参数列表 * @param methodProxy 代理方法 */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.format("[%s] 执行方法 [%s] 参数:[%d][%d]\n", this.getClass().getName(), method.getName(), (Integer)objects[0], (Integer)objects[1]); Object result = methodProxy.invokeSuper(o, objects); System.out.format("[%s] 执行方法 [%s] 结果:[%d]\n", this.getClass().getName(), method.getName(), (Integer)result); return result; } }); // 创建代理对象 ComputerImpl computer = (ComputerImpl)enhancer.create(); // 执行方法 computer.add(1, 2); }}