代理模式

  • 一种设计模式

  • 简单地说,在代理模式中存在三个角色

    • 用户

    • 代理

    • 被代理的对象

  • 用户调用代理,代理去调用被代理的对象

  • 以此来实现功能的增强

  • 动态代理在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);    }}