JDK动态代理

带着疑问看本文:

  1. JDK动态代理为什么只能代理接口,不能代理类?
  2. JDK动态代理如何生成?生成的代理类存放在哪里?

本文不介绍代理模式相关知识,只探讨JDK动态代理的技术问题。

JDK动态代理使用示例

public interface Person {
    void doSomething();
}

public class DefaultPerson implements Person {
    @Override
    public void doSomething() {
        System.out.println("do something");
    }
}

public class PersonHandler implements InvocationHandler {

    private Object target;

    public PersonHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before ...");
        Object ret = null;
        try {
            ret = method.invoke(target, args);
        } catch (Exception e) {
            //todo  handle invoke exception
            System.out.println("after throws ...");
            return ret;
        } 
        System.out.println("after return ...");
        return ret;

    }
}


        DefaultPerson target = new DefaultPerson();
        PersonHandler handler = new PersonHandler(target);

        Person person = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),
                new Class[]{Person.class},
                handler);
        person.doSomething();

通过执行我们看到,生成的代理类对doSomething()方法进行了拦截,执行了PersonHandler中的invoke方法逻辑,从而实现了对doSomething()方法的增强。
再执行如下方法:

System.out.println(person.hashCode());
System.out.println(person.toString());

我们观察到,代理类不仅对我们指定的接口中的方法进行了拦截,对其他方法也进行了拦截。事实上,代理类中所有的方法均被委托给了PersonHandler中的invoke方法。也就是说,代理类的所有方法的执行逻辑都是invoke方法,再由invoke中的method.invoke(target, args)调用目标类上的原方法。

代理类的类型

执行如下代码:

System.out.println(person instanceof DefaultPersonService);
System.out.println(DefaultPerson.class.isAssignableFrom(person.getClass()));
System.out.println(Person.class.isAssignableFrom(person.getClass()));
System.out.println(Proxy.class.isAssignableFrom(person.getClass()));

观察输出结果,思考如下问题:
代理类 与 Person 、DefaultPerson、Proxy 之间是什么关系?
我们再来观察下生成的类是什么样子:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.zzvcom.lspring.beans.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void doSomething() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.zzvcom.lspring.beans.Person").getMethod("doSomething");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

我们可以看到:

  1. 代理类是final的,不能再被继承
  2. 代理类是我们指定接口的实现类
  3. 代理类继承了Proxy类,是Proxy的子类
  4. 代理类有一个构造方法,其参数是InvocationHandler
  5. 代理类把equalstoStringhashCode以及指定接口中的doSomething方法全都委托给InvocationHandler执行了

此外,通过观察每个方法中都有super.h.invoke(this, m1, ..)调用,可以看到,invoke的第一个参数this指的是当前代理类的实例,第二个参数m指定的原始对象或Object中声明的方法,因为他们都是通过反射从原始对象中获取的。代理类对equalstoStringhashCode这些放Object中声明的方法进行了重写,那么Object中的其他方法呢?如wait(),notify(),getClass(),clone(),finilize()等。。。
wait(),notify(),getClass()是final方法,无法被重写,对其增强也就无从谈起。
clone()finilize()呢?

代理类的生成过程

查看Proxy.newProxyInstance(..)的源码:

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);
        }
    }
  1. 该方法通过getProxyClass0(loader, intfs)生成代理类并使用loader类加载器加载该类形成Class文件
  2. 通过cl.getConstructor(constructorParams)拿到类的构造方法,注意该构造方法以InvocationHandler为参数。
  3. 通过cons.newInstance(new Object[]{h})执行构造方法,创建类实例。

再看getProxyClass0函数的源码:

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
        return proxyClassCache.get(loader, interfaces);
    }

直接使用proxyClassCache获取Class文件。proxyClassCache是一个WeakCache,其Key是ClassLoader,Value是Class对象。具体的代理类生成逻辑在ProxyClassFactory中:

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
  1. 尝试加载所有的接口类,如果有接口无法加载,则直接报错
  2. 对所有接口进行访问性验证,如果有接口来自其他包且不是public则直接报错
  3. 使用ProxyGenerator.generateProxyClass(..)生成类的二进制字节码
  4. 调用native方法defineClass0(..)生成Class文件

从生成过程中我们可以看到,生成的Class类是弱引用,也就意味着当JVM空间不足时,这些代理类会被垃圾回收器回收,再次使用时需要重新生成。

CGLib动态代理相关文章:
https://objectcomputing.com/resources/publications/sett/november-2005-create-proxies-dynamically-using-cglib-library
https://www.ffutop.com/posts/2018-07-10-cglib-enhancer/
https://dzone.com/articles/cglib-missing-manual

posted @ 2023-01-31 15:02  小张同学哈  阅读(42)  评论(0)    收藏  举报