java 代理模式实现

代理

代理模式

代理模式是一种结构型设计模式,概念为通过一个代理对象来控制对原始对象的访问,代理对象通常表现出的行为与原始对象一致(实现同一接口)以便于可以在使用原始对象的地方代替原始对象。使用代理模式一般用来保护以及增强原始对象。

静态代理与动态代理

编译期间就确定好了代理类与被代理类为静态代理,其中代理类与被代理类会实现同一个接口,代理类持有被代理类,代理类可以调用被代理类方法并添加逻辑。

动态代理的代理类是运行期间动态生成的,其他地方与静态代理相同。很显然在被代理类很多时使用动态代理可以节省代码且方便修改逻辑。

静态代理一般来说效率稍高,但是代码冗余大,不便于修改。

动态代理一般使用 jdk 动态代理和 cglib 动态代理。

jdk 动态代理

jdk 动态代理是基于实现接口的方式,代理类是动态生成的和被代理类实现同一接口且持有被代理类实例的类。

很显然 jdk 动态代理是代理不了接口中没有的方法的。

jdk 动态代理基于反射,代理类与被代理类是委托关系,使用 Proxy 生成字节码文件。

涉及到以下几个类和接口

InvocationHandler 接口

动态代理类需要实现这个接口且持有被代理类实例,通过代理对象调用一个方法时,这个方法的调用会被转发由这个接口的 invoke 方法来进行调用

java 复制代码
// InvocationHandler 唯一的接口
Object invoke(Object proxy, Method method, Object[] args)throws Throwable 

参数说明:

  • proxy - 被代理类实例
  • method - 被代理类的某个方法的 Method 对象
  • args - 调用被代理类某个方法时需要的参数列表
Proxy 类

Proxy 这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,用的最多的就是 newProxyInstance 方法:

java 复制代码
//得到一个动态的代理对象
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

参数说明:

  • loader - 一个 ClassLoader 对象,定义了由哪个 ClassLoader 对象来对生成的代理对象进行加载
  • interfaces - 一个接口对象的数组,表示代理对象需要实现的接口
  • h - 一个 InvocationHandler 对象,表示代理对象调用方法时需要关联的 InvocationHandler 对象
动态生成的类

只会生成一个类,这是其反编译后的代码示例

java 复制代码
//继承了java.lang.reflect.Proxy,实现了被代理类的接口
public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) {
        //使用父类的构造方法
        super(var1);
    }
    
    ...

    public final void deal() {
        try {
            //调用InvocationHandler的invoke方法
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    ...

    static {
        try {
            //Object的equals方法
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            //Object的toString方法
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            //被代理类的接口中的方法,如果接口中有多个方法会往后延
            m3 = Class.forName("动态代理.jdk动态代理.Subject").getMethod("deal");
            //Object的hashCode方法
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

其调用关系为

调用方 -> 动态生成的代理类的对应方法 -> 拦截器的 invoke 方法 -> 被代理类的对应方法

cglib 动态代理

cglib 动态代理是基于继承的方式,代理类是动态生成的继承了被代理类的类,使用重写方法的方式实现增强。

由于 cglib 动态代理是基于继承的,因此代理不了私有方法、final 方法、静态方法和构造方法,以及被声明为 final 的类。

cglib 动态代理基于FastClass方法索引,第一次调用需要生成 Class 对象较慢,代理类与被代理类是继承关系,使用ASM框架生成字节码文件。

MethodInterceptor

拦截器需要实现的接口,通过代理对象调用一个方法时,这个方法的调用会被转发由这个接口的 intercept 方法来进行调用

java 复制代码
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable

参数说明:

  • o - 代理类
  • method - 被代理的方法
  • args - 调用时的参数
  • methodProxy - 方法的代理类
FastClass

索引类,其中有两个主要的方法

  • getIndex - 获取目标的编号;索引类记录了目标方法与编号之间的对应关系,传递方法签名信息并返回对应编号
  • invoke - 根据方法编号,调用目标方法;三个参数分别代表
    • index - 方法编号
    • target - 目标对象
    • args - 方法执行所需参数
java 复制代码
//代理类的FastClass
public class RealSubject$$FastClassByCGLIB$$b86fb30b extends FastClass {
    public RealSubject$$FastClassByCGLIB$$b86fb30b(Class var1) {
        super(var1);
    }

    //根据方法的签名获取对应的索引
    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
            case 1540437897:
                if (var10000.equals("deal()V")) {
                    return 0;
                }
                break;
            ...
        }

        return -1;
    }

    ...

    //根据方法索引调用被索引类的对应方法
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        RealSubject var10000 = (RealSubject)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
                case 0:
                    var10000.deal();
                    return null;
                ...
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
	...
}
MethodProxy

在代理类初始化时被创建

java 复制代码
public class RealSubject$$EnhancerByCGLIB$$679604bd extends RealSubject implements Factory {
    
    ...
    
    private static Method CGLIB$deal$0$Method;
    private static MethodProxy CGLIB$deal$0$Proxy;
    
    ...
    
    static void CGLIB$STATICHOOK1() throws ClassNotFoundException {
        ...
        CGLIB$deal$0$Method = ReflectUtils.findMethods(new String[]{"deal", "()V"}, (var1 = Class.forName("动态代理.cglib动态代理.RealSubject")).getDeclaredMethods())[0];
        //关联了代理的方法与被代理的方法
        CGLIB$deal$0$Proxy = MethodProxy.create(var1, var0, "()V", "deal", "CGLIB$deal$0");
    }
    
    static {
        try {
            CGLIB$STATICHOOK1();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在 invoke 或者 invokeSuper 方法调用时被初始化

java 复制代码
public class MethodProxy {
    public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            //初始化
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        } catch (IllegalArgumentException var5) {
            if (this.fastClassInfo.i1 < 0) {
                throw new IllegalArgumentException("Protected method: " + this.sig1);
            } else {
                throw var5;
            }
        }
    }
    
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            //初始化
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
    
    private void init() {
        //如果没有初始化的话
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    MethodProxy.CreateInfo ci = this.createInfo;
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    //获取代理类和被代理类的索引类,其中f1是被代理类的索引类,f2是代理类的索引类
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    //类似的,i1是被代理方法的索引值,i2是代理类的索引值
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }
    }
}
动态生成的类

会生成三个类,分别是代理类、代理类的索引类、被代理类的索引类

java 复制代码
//动态生成的代理类
public class RealSubject$$EnhancerByCGLIB$$679604bd extends RealSubject implements Factory {
    ...
    //直接调用被代理方法的方法
    final void CGLIB$deal$0() {
        super.deal();
    } 
        
    //重写的被代理类的方法
    public final void deal() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            try {
                //调用拦截器的intercept方法
                var10000.intercept(this, CGLIB$deal$0$Method, CGLIB$emptyArgs, CGLIB$deal$0$Proxy);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        } else {
            super.deal();
        }
    }
    ...
}

//代理类的FastClass
public class RealSubject$$EnhancerByCGLIB$$679604bd$$FastClassByCGLIB$$d3b917bf extends FastClass {
    ...
}

//代理类的FastClass
public class RealSubject$$FastClassByCGLIB$$b86fb30b extends FastClass {
}

其调用关系为

调用方 -> 动态生成的代理类的对应方法的重载 -> 自定义的拦截器的 intercept 方法 -> 代理类索引类的 invoke 方法 -> 动态生成的代理类的直接调用父类对应方法的实现 -> 被代理类
调用方 代理类 拦截器 MethodProxy 代理类索引类 被代理类 业务方法() 1. intercept() 2. 前置拦截 3. invokeSuper() 4. getIndex() 5. 返回方法索引 6. invoke() 7. CGLIB业务方法?() 8. 业务方法 9. 返回 10. 返回 11. 返回 12. 返回 13. 后置拦截 14. 返回 15. 返回 调用方 代理类 拦截器 MethodProxy 代理类索引类 被代理类

相关推荐
稻草猫.12 小时前
Java网络编程套接字
java·后端·java-ee·idea
云泽80814 小时前
函数模板与类模板:C++泛型编程核心解析
java·开发语言·c++
缺点内向17 小时前
Java:创建、读取或更新 Excel 文档
java·excel
带刺的坐椅18 小时前
Solon v3.4.7, v3.5.6, v3.6.1 发布(国产优秀应用开发框架)
java·spring·solon
四谎真好看19 小时前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
应用市场19 小时前
构建自定义命令行工具 - 打造专属指令体
开发语言·windows·python
桦说编程19 小时前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t19 小时前
ZIP工具类
java·zip
lang2015092820 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
Dfreedom.20 小时前
一文掌握Python四大核心数据结构:变量、结构体、类与枚举
开发语言·数据结构·python·变量·数据类型