代理模式
- 一种设计模式
- 简单地说,在代理模式中存在三个角色
- 用户调用代理,代理去调用被代理的对象
- 以此来实现功能的增强
- 动态代理在java中有两种实现方法
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
- 方法拦截器会对被代理的目标类中所有可以增强的方法进行增强
- 方法拦截器的核心方法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);
- }
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |