动态代理随笔

动态代理随笔

代理模式简介

代理模式是什么

  • 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
  • 说人话就是,多创建一个代理对象,通过调用代理对象去完成原先直接调用真实对象完成的任务, 在此期间,代理对象可以对原有操作进行增强

代理模式组成

代理模式的组成基于三个角色

角色功能
抽象角色通过接口或者抽象类声明真实角色实现的业务方法
代理角色实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作
真实角色实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用

以下是代理模式的类图

image-20230726221641080

代理模式实现

动态代理一般可分为静态代理和动态代理

静态代理

在编码期就知道确定了代理对象,需要为每一个被代理对象创建一个代理类。

静态代理的实现比较简单,但是每个被代理对象都需要创建一个代理类,因此在代理对象比较多时,会导致代码几余和维护成本增加。

以下是静态代理的基础实现:

public class Main {
    public static void main(String[] args) {
        ISomething instance = new SomethingProxy(new Something());
        instance.doSomething();
    }

    interface ISomething {
        void doSomething();
    }

    static class Something implements ISomething {
        @Override
        public void doSomething() {
            System.out.println("do something");
        }
    }

    static class SomethingProxy implements ISomething {
        private ISomething target;

        public SomethingProxy(ISomething target) {
            this.target = target;
        }

        @Override
        public void doSomething() {
            System.out.println("before");
            target.doSomething();
            System.out.println("after");
        }
    }
}
动态代理

动态代理是一种代理模式的实现方式,它在运行期间根据需要动态生成代理对象,无需手动编写代理类,可以减少代码几余和维护成本。

动态代理适用于需要代理的对象数量较多,代理类实现相对灵活的场景,例Spring框架中的Spring AOP(面向切面编程)功能。

Java自带的动态代理实现

动态代理最简单的实现依赖于 java.lang.reflect 包下 Proxy 类和 InvocationHandler 接口

Proxy

image-20230726223104215

对于Proxy类我们需要使用的是Proxy.newProxyInstance()方法

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) throws IllegalArgumentException

方法有三个参数:

  • ClassLoader loader: 用哪个类加载器去加载代理对象

  • Class<?>[] interfaces:动态代理类需要实现的接口

  • InvocationHandler h:动态代理方法在执行时,会调用InvocationHandler里面的invoke方法去执行

  • loader我们一般直接使用被代理类的类加载器就好了

  • InvocationHandler 则是我们需要实现的接口, 对于代理对象的增强就是通过重写InvocationHandler.invoke()方法实现

newProxyInstance方法源码分析

简单来说就是先看缓存里有没有已经创建的代理对象, 有就直接返回,没就创建

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
         * Look up or generate the designated proxy class.
         */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
         * Invoke its constructor with the designated invocation handler.
         */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}
Objects.requireNonNull(h);
  1. 参数校验,没啥好说的
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
    checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
  1. 先克隆了一份传入接口数组的副本,用于接下来安全检查
  2. System.getSecurityManager() 获取当前的安全管理器
  3. 如果安全管理器不为null则进行安全检查
  4. 安全检查内容如下(来自源码注释)

* Check permissions required to create a Proxy class.
*
* To define a proxy class, it performs the access checks as in
* Class.forName (VM will invoke ClassLoader.checkPackageAccess):
* 1. “getClassLoader” permission check if loader == null
* 2. checkPackageAccess on the interfaces it implements
*
* To get a constructor and new instance of a proxy class, it performs
* the package access check on the interfaces it implements
* as in Class.getConstructor.
*
* If an interface is non-public, the proxy class must be defined by
* the defining loader of the interface. If the caller’s class loader
* is not the same as the defining loader of the interface, the VM
* will throw IllegalAccessError when the generated proxy class is
* being defined via the defineClass0 method.

检查创建Proxy类所需的权限。

要定义一个Proxy类,它会执行访问检查,就像Class.forName一样(虚拟机将调用ClassLoader.checkPackageAccess):

  1. 如果loader == null,则会检查 “getClassLoader” 权限
  2. 对其实现的接口执行checkPackageAccess

要获取代理类的构造函数和新实例,它会执行对其实现的接口执行包访问检查,就像Class.getConstructor一样。

如果一个接口是非公开的,则代理类必须由该接口的定义加载器定义。如果调用者的类加载器与接口的定义加载器不相同,

则在通过defineClass0方法定义生成的代理类时,虚拟机将抛出IllegalAccessError。

/*
 * Look up or generate the designated proxy class.
 * 查找或生成指定的代理类。
 */
Class<?> cl = getProxyClass0(loader, intfs);
private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    // 如果指定加载器定义的代理类实现
    // 给定的接口存在,这将简单地返回缓存副本;
    // 否则,它将通过ProxyClassFactory创建代理类
    return proxyClassCache.get(loader, interfaces);
}

看注释就知道干嘛了

if (sm != null) {
    checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
private static void checkNewProxyPermission(Class<?> caller, Class<?> proxyClass) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        if (ReflectUtil.isNonPublicProxyClass(proxyClass)) {
            ClassLoader ccl = caller.getClassLoader();
            ClassLoader pcl = proxyClass.getClassLoader();

            // do permission check if the caller is in a different runtime package
            // of the proxy class
            int n = proxyClass.getName().lastIndexOf('.');
            String pkg = (n == -1) ? "" : proxyClass.getName().substring(0, n);

            n = caller.getName().lastIndexOf('.');
            String callerPkg = (n == -1) ? "" : caller.getName().substring(0, n);

            if (pcl != ccl || !pkg.equals(callerPkg)) {
                sm.checkPermission(new ReflectPermission("newProxyInPackage." + pkg));
            }
        }
    }
}
  1. sm 是一个安全管理器(SecurityManager)对象,用于进行安全管理操作。
  2. System.getSecurityManager() 用于获取当前运行的 Java 虚拟机的安全管理器。如果安全管理器不存在,则返回 null
  3. ReflectUtil.isNonPublicProxyClass(proxyClass) 是一个自定义的辅助方法,用于检查生成的代理类是否为非公开类。非公开类是指不是公开的(non-public)类,即只能在定义它们的包内部访问。
  4. proxyClass 是生成的代理类。
  5. caller 是调用这段代码的类。
  6. caller.getClassLoader()proxyClass.getClassLoader() 分别获取调用者和代理类的类加载器。
  7. 后面的部分执行权限检查。它会比较调用者的类加载器和代理类的类加载器是否相同,以及两者是否在不同的运行时包中。如果代理类和调用者不在同一个运行时包中,就会执行权限检查。
  8. 如果代理类和调用者不在同一个运行时包中,则会使用 ReflectPermission 进行权限检查。具体检查的权限为 "newProxyInPackage." + pkg,其中 pkg 是代理类的运行时包名。这样的权限检查可以确保只有在特定运行时包中的代码可以创建代理实例。

这段代码的目的是在安全的环境中创建代理类,并且只有满足特定条件的授权代码才能执行这一操作。

final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            cons.setAccessible(true);
            return null;
        }
    });
}
return cons.newInstance(new Object[]{h});
  1. cl 是一个 ClassLoader 对象,用于加载代理类。
  2. constructorParams 是一个 Class<?>[] 数组,表示代理类的构造函数参数。
  3. h 是一个 InvocationHandler 对象,用于处理代理对象的方法调用。
  4. cons 是代理类的构造函数,通过 cl.getConstructor(constructorParams) 获取。
  5. ih 是传入的 InvocationHandler 对象的引用,用于构造代理对象时使用。
  6. Modifier.isPublic(cl.getModifiers()) 检查代理类的修饰符是否为 public。如果代理类不是公开类,将需要特殊的权限来设置其可访问性。
  7. 如果代理类不是公开类,则会使用 AccessController.doPrivileged 方法来执行特权操作,这是一个特殊的权限获取机制。
  8. PrivilegedActionrun() 方法中,通过 cons.setAccessible(true) 将构造函数设置为可访问,以便在非公开类的情况下创建实例。
  9. 最后,使用 cons.newInstance(new Object[]{h}) 创建代理类的实例,并将 h 作为构造函数参数传入。这样,通过 InvocationHandler 对象 h 来处理代理对象的方法调用。

总的来说,这段代码片段用于创建代理类的实例,并且在需要的情况下通过特权机制设置构造函数的可访问性,以确保代理类可以在非公开类的情况下正确创建实例。这样的代码通常在生成代理类的过程中涉及到访问权限的处理时使用。

InvocationHandler

image-20230726223125892

image-20230726231707684

基础的实现代码
public class Main {
    public static void main(String[] args) {
        BaseInterface instance = (BaseInterface) Proxy.newProxyInstance(Base.class.getClassLoader(),
                new Class[]{BaseInterface.class},
                new BaseInvocationHandler(new Base()));

        instance.method();
    }

    interface BaseInterface {
        void method();
    }

    static class Base implements BaseInterface {
        public void method() {
            System.out.println("Base method");
        }
    }

    static class BaseInvocationHandler implements InvocationHandler {
        private Base target;

        public BaseInvocationHandler() {
        }

        public BaseInvocationHandler(Base target) {
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before");
            Object result = method.invoke(target, args);
            System.out.println("after");
            return result;
        }
    }
}

CGLib实现

[CGLib文档](cglib 3.3.0 javadoc (cglib))

基础的实现
public class Main {
    public static void main(String[] args) {
        Something something = (Something) Enhancer.create(Something.class, new SomethingMethodInterceptor());
        something.doSomething();	
    }
}

class Something {
    void doSomething() {
        System.out.println("do something");
    }
}

class SomethingMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("before do something");
        Object ret = methodProxy.invokeSuper(o, objects);
        System.out.println("after do something");

        return ret;
    }
}

接下来通过基础的实现逐步深入了解CGLib动态代理

Enhancer
Something something = (Something) Enhancer.create(Something.class, new SomethingMethodInterceptor());

Enhancer 类的作用类似于 Java reflect包下的 Proxy 类, 是用来创建代理对象的

image-20230727101449056

通过文档可以知道 Enhancer 创建代理对象有多个方法

  • 文档中对于静态创建代理对象的方法都加入了以下说明
    • 为了对生成的实例有更精细的控制,使用一个新的实例,而不是这个静态方法 Enhancer
  • 直接看参数最多的创建方法
public static Object create(Class superclass,
                            Class[] interfaces,
                            CallbackFilter filter,
                            Callback[] callbacks)
  • 参数:

    superclass - 要扩展的类或要实现的接口。

    interfaces - 要实现的接口数组,或者为null。

    filter - 在生成新类时要使用的回调过滤器。

    callbacks - 用于增强对象的回调实现。

一二两个参数很好理解, 类似于Proxy.newInstance()的参数

先看第四个参数 要求传入的是一个 Callback[]

Callback

Callback 是 cglib 中的一个接口, 其本身没有定义任何需要实现的方法,其底下有7个子接口, 分别可以实现不同的增强功能

image-20230727104911975

MethodInterceptor

类似于JDK中的 InvocationHandler 接口。

public interface MethodInterceptor
extends Callback
{
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
}

前三个参数与InvocationHandler.invoke()一样 分别是

  • obj - “this”,即被增强的对象。
  • method - 被拦截的方法。
  • args - 参数数组;原始数据类型会被封装。

而第四个参数 MethodProxy proxy CGLib 对其的描述是

可以通过正常的反射方式使用Method对象来调用原始方法,也可以通过使用MethodProxy(更快速)来调用。

可以通过 methodProxy.invokeSuper(Object obj, Object[] args) 调用传入对象父类的方法,在这里传入代理对象就可以调用原对象的初始方法

FixedValue
public interface FixedValue extends Callback {
    Object loadObject() throws Exception;
}

通过实现这个接口, 可以使代理对象执行代理方法时每次返回的都是一个固定的值.

注意: loadObject 的返回值要与原方法的返回值能够进行类型转换, 否则会抛出java.lang.ClassCastException 异常

public class Main {
    public static void main(String[] args) {
        Something something = (Something) Enhancer.create(Something.class, new SomethingFixedValue());

        System.out.println(something.returnInt()); // 99999
        System.out.println(something.returnDouble()); // 99999.0
        System.out.println(something.returnString()); // java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    }

    static class Something {
        int returnInt() {
            return 1;
        }

        double returnDouble() {
            return 1.0;
        }

        String returnString() {
            return "1";
        }
    }

    static class SomethingFixedValue implements FixedValue {
        @Override
        public Object loadObject() throws Exception {
            return 99999;
        }
    }
}
LazyLoader

见名知意, 这是一个用来实现懒加载的接口

public interface LazyLoader extends Callback {
    Object loadObject() throws Exception;
}

LazyLoader.loadObject()方法用于创建需要懒加载的对象,而这一个方法只会在第一次访问懒加载对象方法时被调用。

从例子来理解

public class LazyLoaderDemo {
    public static void main(String[] args) {
        SomethingNeedLazyLoader somethingNeedLazyLoader = new SomethingNeedLazyLoader();
        System.out.println("===========================");
        somethingNeedLazyLoader.doSomething();
        System.out.println("===========================");
        somethingNeedLazyLoader.doSomething();
    }
}

class Something {
    void doSomething() {
        System.out.println("do something");
    }
}

class SomethingLazyLoader implements LazyLoader {
    @Override
    public Object loadObject() throws Exception {
        System.out.println("lazy load");
        return new Something();
    }
}

class SomethingNeedLazyLoader {
    private Something something;

    SomethingNeedLazyLoader() {
        System.out.println("SomethingNeedLazyLoader created");
        this.something = (Something) Enhancer.create(Something.class, new SomethingLazyLoader());
    }

    void doSomething() {
        something.doSomething();
    }
}

这段代码的执行结果为

SomethingNeedLazyLoader created
===========================
lazy load
do something
===========================
do something

也就是说当SomethingNeedLazyLoader 执行构造方法时 虽然执行了

this.something = (Something) Enhancer.create(Something.class, new SomethingLazyLoader());

但是没有执行 SomethingLazyLoader.loadObject()方法

直到执行something.doSomething();SomethingLazyLoader.loadObject()方法才被第一次执行。

当第二次执行something.doSomething();时 可以发现SomethingLazyLoader.loadObject()并没有被再次执行。

注意:

  1. SomethingNeedLazyLoader对象创建时,something对象就已经创建出来了,可以通过调试看到

image-20230727133919778

  1. 只有调用懒加载对象的方法时,SomethingLazyLoader.loadObject()才会执行
public class LazyLoaderDemo {
    public static void main(String[] args) {
        SomethingNeedLazyLoader somethingNeedLazyLoader = new SomethingNeedLazyLoader();
        System.out.println("===========================");
        somethingNeedLazyLoader.getSomething().someField = "some field";
        System.out.println("===========================");
        System.out.println(somethingNeedLazyLoader.getSomething().someField);
        System.out.println("===========================");
        somethingNeedLazyLoader.getSomething().setSomeField("some field");
        System.out.println("===========================");
        System.out.println(somethingNeedLazyLoader.getSomething().getSomeField());

    }
}
SomethingNeedLazyLoader created
===========================
===========================
some field
===========================
lazy load
===========================
some field

可以看到,当直接对懒加载对象的属性进行访问或修改时,并没有执行loadObject()

Dispatcher

Dispatcher与LazyLoader一样,也是一种延迟加载接口,与LazyLoader不同的是, Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法。其他特性都与LazyLoader类似

public interface Dispatcher extends Callback {
    Object loadObject() throws Exception;
}
public class DispatcherDemo {
    public static void main(String[] args) {
        Something something = (Something) Enhancer.create(Something.class, new SomethingDispatcher());
        System.out.println("===========================");
        something.doSomething();
        System.out.println("===========================");
        something.doSomething();
    }

    static class Something {
        void doSomething() {
            System.out.println("do something");
        }
    }

    static class SomethingDispatcher implements net.sf.cglib.proxy.Dispatcher {
        @Override
        public Object loadObject() throws Exception {
            System.out.println("Dispatcher loadObject");
            return new Something();
        }
    }
}

执行结果

===========================
Dispatcher loadObject
do something
===========================
Dispatcher loadObject
do something
NoOp

见名知意, 就是啥也不干, 代理对象并不会对原对象进行任何操作

public interface NoOp extends Callback
{
    /**
     * A thread-safe singleton instance of the NoOp callback.
     * NoOp回调的线程安全的单例实例
     */
    public static final NoOp INSTANCE = new NoOp() { };
}
public class NoOpDemo {
    public static void main(String[] args) {
        Something something = (Something) Enhancer.create(Something.class, NoOp.INSTANCE);
        something.doSomething(); // do something
    }

    static class Something {
        void doSomething() {
            System.out.println("do something");
        }
    }
}
InvocationHandler

就是JDK中reflect包下的 InvocationHandler 接口的替换, 主打的就是一个一模一样

public interface InvocationHandler
extends Callback
{
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
CallbackFilter

回到开头的CallbackFilter参数

CallbackFilter: 回调过滤器

发现了吗?Enhancer.create 的第四个参数接受的是Callback[]是一个数组,也就是说,对于同一个代理对象来说, 他可以有不同的增强方式,要如何实现不同的回调逻辑呢?这里就要用到CallbackFilter

public interface CallbackFilter {
    int accept(Method method); // 返回的值为数字,代表了Callback数组中的索引位置
    boolean equals(Object o);
}

通过重写accept方法就能实现定义不同的回调逻辑

public class CallbackFilterDemo {
    public static void main(String[] args) {
        Something something =
                (Something) Enhancer.create(Something.class,
                    null ,
                    new SomethingCallbackFilter(),
                    new Callback[]{new SomethingMethodInterceptor(), NoOp.INSTANCE});
        something.method1();
        System.out.println("===========================");
        something.method2();
        System.out.println("===========================");
        something.method3();
    }
}

class Something {
    void method1() {
        System.out.println("method1");
    }

    void method2() {
        System.out.println("method2");
    }

    void method3() {
        System.out.println("method3");
    }
}

class SomethingMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, java.lang.reflect.Method method, Object[] objects, net.sf.cglib.proxy.MethodProxy methodProxy) throws Throwable {

        System.out.println("MethodInterceptor");
        return methodProxy.invokeSuper(o, objects);
    }
}

class SomethingCallbackFilter implements CallbackFilter {

    @Override
    public int accept(java.lang.reflect.Method method) {
        if (method.getName().equals("method1")) {
            return 0;
        } else {
            return 1;
        }
    }
}
MethodInterceptor
method1
===========================
method2
===========================
method3

细节:

  1. 使用场景: CallbackFilter常用于创建复杂的代理对象,其中不同的方法可能需要不同的拦截或增强逻辑。通过实现CallbackFilter接口,可以根据方法名、参数类型、访问修饰符等等来进行细粒度的拦截和增强。
  2. 优化代理对象生成: 使用CallbackFilter还有一个重要的优点是可以优化代理对象的生成。在CGLIB中,为了节省内存和提高性能,对于同一个目标类和相同的回调数组,CGLIB会复用代理类。这意味着,如果CallbackFilter相同,那么生成的代理类也会相同,从而避免重复生成代理类。

注意:

  • 在实现CallbackFilter时,请确保根据方法的调用情况返回正确的Callback索引。如果返回了无效的索引,可能会导致运行时异常或不正确的代理行为。
  • 考虑性能因素,尽量避免在accept方法中进行复杂的计算或耗时操作,以确保代理对象的生成和方法调用具有良好的性能。

至此,CGLib动态代理的基础实现就讲完了, 还有别的例如InterfaceMaker CallbackHelper之类的,看文档就知道怎么用了。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值