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 代理类索引类 被代理类

相关推荐
工程师小星星2 小时前
Golang语言的文件组织方式
开发语言·后端·golang
张子夜 iiii2 小时前
实战项目-----Python+OpenCV 实现对视频的椒盐噪声注入与实时平滑还原”
开发语言·python·opencv·计算机视觉
2301_770373732 小时前
Java集合
java·开发语言
哈喽姥爷2 小时前
Spring Boot---自动配置原理和自定义Starter
java·spring boot·后端·自定义starter·自动配置原理
啊?啊?3 小时前
18 从对象内存到函数调用:C++ 虚函数表原理(继承覆盖 / 动态绑定)+ 多态实战
开发语言·c++·多态原理
bkspiderx3 小时前
C++标准库:文件流类
开发语言·c++
siy23333 小时前
[c语言日记] 数组的一种死法和两种用法
c语言·开发语言·笔记·学习·链表
njxiejing4 小时前
Python NumPy安装、导入与入门
开发语言·python·numpy
Rhys..4 小时前
Python&Flask 使用 DBUtils 创建通用连接池
开发语言·python·mysql