JDK 动态代理实现及原理解析

来源:互联网 发布:js改变display属性 编辑:程序博客网 时间:2024/06/13 03:32

前面介绍了代理模式,讲了动态代理常见的实现方式,包含了JDK的动态代理和CGLib的动态代理。本文将介绍下JDK 动态代理实现及机制。

首先,要了解的是InvocationHandler接口。

"InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler."

InvocationHandler是每个完成方法调用处理的代理实例需要实现的接口。每个代理实例都有关联的一个调用处理者。当一个方法被代理实例执行时,方法调用会被转发到调用处理者执行。

这个接口有一个方法:

    public Object invoke(Object proxy, Method method, Object[] args)        throws Throwable;

其中proxy是生成代理的对象,method是方法,args是方法参数。来看下具体用法:

/** * Create by zxb on 2017/4/23 */public interface IDBQuery {    String getElement(String id);}/** * Create by zxb on 2017/4/23 */public class DBQuery implements IDBQuery {    public String getElement(String id) {        return id + "_JDKProxy";    }}/** * Create by zxb on 2017/4/23 */public class DBQueryProxy implements InvocationHandler {    private DBQuery dbQuery;    public DBQueryProxy(DBQuery dbQuery) {        this.dbQuery = dbQuery;    }    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {        return method.invoke(dbQuery, objects);    }}/** * Create by zxb on 2017/4/23 */public class TestJDKProxy {    public static void main(String[] args) {        DBQuery dbQuery = new DBQuery();        DBQueryProxy dbQueryProxy = new DBQueryProxy(dbQuery);        IDBQuery query = (IDBQuery) Proxy                .newProxyInstance(dbQuery.getClass().getClassLoader(), dbQuery.getClass().getInterfaces(),                        dbQueryProxy);        System.out.println(query.getElement("Hello"));    }}

执行结果:



其中需要注意的是,实现了InvocationHandler接口的DBQueryProxy,依赖真实对象。开始的时候,对这个不解,但是后来有了了解,才知道是需要这么写的。如果使用method.invoke(o,args)那么就会变成死循环,不断调自己。后面有解释。

所以看完例子,这里最重要的就是

Proxy.newProxyInstance(dbQuery.getClass().getClassLoader(), dbQuery.getClass().getInterfaces(), dbQueryProxy);
看下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);        }        /*         * 查找 or 生成 指定的代理类,这步有用到缓存         */        Class<?> cl = getProxyClass0(loader, intfs);        /*         * 通过指定的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);        }    }
通过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的定义为

   private static final WeakCache<ClassLoader, Class<?>[], Class<?>>        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());  

这个WeakCache可以根据传入的ClassLoader、Class<?>[] 接口类数组得到对应的key和代理的类,有点类似map。所以重点看ProxyClassFactory。

private static final class ProxyClassFactory        implements BiFunction<ClassLoader, Class<?>[], Class<?>>    {        // 所有生成的代理类的前缀        private static final String proxyClassNamePrefix = "$Proxy";        // 下一个代理类的唯一标识值        private static final AtomicLong nextUniqueNumber = new AtomicLong();        @Override        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {            //校验传入的接口数组,是否由loader加载的?是否是接口?是否重复?            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;     // 定义代理存放的包            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;            /*             * 处理非public类的接口,到同一包中             */            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 + ".";            }            /*             * 设置代理类的名称             */            long num = nextUniqueNumber.getAndIncrement();            String proxyName = proxyPkg + proxyClassNamePrefix + num;            /*             * 生成代理类             */            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());            }        }    }

OK,可以看到,关键是在

ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
来看ProxyGenerator的generateProxyClass方法传入代理类名称、接口、访问控制标志

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);        //这步生成对应代理类的byte数组        final byte[] var4 = var3.generateClassFile();        //saveGeneratedFiles的值,由sun.misc.ProxyGenerator.saveGeneratedFiles变量值决定        //保存生成的文件,否则直接返回byte数组      if(saveGeneratedFiles) {            AccessController.doPrivileged(new PrivilegedAction() {                public Void run() {                    try {                        int var1 = var0.lastIndexOf(46);                        Path var2;                        if(var1 > 0) {                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), new String[0]);                            Files.createDirectories(var3, new FileAttribute[0]);                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");                        } else {                            var2 = Paths.get(var0 + ".class", new String[0]);                        }                        Files.write(var2, var4, new OpenOption[0]);                        return null;                    } catch (IOException var4x) {                        throw new InternalError("I/O exception saving generated file: " + var4x);                    }                }            });        }        return var4;    }
我们来看下,如何生成代理类byte数组
private byte[] generateClassFile() {        添加hascode,equals,toString()方法        this.addProxyMethod(hashCodeMethod, Object.class);        this.addProxyMethod(equalsMethod, Object.class);        this.addProxyMethod(toStringMethod, Object.class);        Class[] var1 = this.interfaces;        int var2 = var1.length;        int var3;        Class var4;        //遍历接口,添加方法       for(var3 = 0; var3 < var2; ++var3) {            var4 = var1[var3];            Method[] var5 = var4.getMethods();            int var6 = var5.length;            for(int var7 = 0; var7 < var6; ++var7) {                Method var8 = var5[var7];                this.addProxyMethod(var8, var4);            }        }        Iterator var11 = this.proxyMethods.values().iterator();        List var12;        while(var11.hasNext()) {            var12 = (List)var11.next();            checkReturnTypes(var12);        }        Iterator var15;        try {            //添加构造函数           this.methods.add(this.generateConstructor());            var11 = this.proxyMethods.values().iterator();            while(var11.hasNext()) {                var12 = (List)var11.next();                var15 = var12.iterator();                //添加方法及字段                while(var15.hasNext()) {                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));                    this.methods.add(var16.generateMethod());                }            }            //添加静态初始化语句            this.methods.add(this.generateStaticInitializer());        } catch (IOException var10) {            throw new InternalError("unexpected I/O Exception", var10);        }        if(this.methods.size() > '\uffff') {            throw new IllegalArgumentException("method limit exceeded");        } else if(this.fields.size() > '\uffff') {            throw new IllegalArgumentException("field limit exceeded");        } else {            this.cp.getClass(dotToSlash(this.className));            this.cp.getClass("java/lang/reflect/Proxy");            var1 = this.interfaces;            var2 = var1.length;            for(var3 = 0; var3 < var2; ++var3) {                var4 = var1[var3];                this.cp.getClass(dotToSlash(var4.getName()));            }            this.cp.setReadOnly();            ByteArrayOutputStream var13 = new ByteArrayOutputStream();            DataOutputStream var14 = new DataOutputStream(var13);            try {                var14.writeInt(-889275714);                var14.writeShort(0);                var14.writeShort(49);                this.cp.write(var14);                var14.writeShort(this.accessFlags);                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));                var14.writeShort(this.interfaces.length);                Class[] var17 = this.interfaces;                int var18 = var17.length;                for(int var19 = 0; var19 < var18; ++var19) {                    Class var22 = var17[var19];                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));                }                var14.writeShort(this.fields.size());                var15 = this.fields.iterator();                while(var15.hasNext()) {                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();                    var20.write(var14);                }                var14.writeShort(this.methods.size());                var15 = this.methods.iterator();                while(var15.hasNext()) {                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();                    var21.write(var14);                }                var14.writeShort(0);                return var13.toByteArray();            } catch (IOException var9) {                throw new InternalError("unexpected I/O Exception", var9);            }        }    }

在前面的Test类中,添加

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

重新执行,可以在项目的根目录中多了com文件夹,点进去可以在 com\sun\proxy\目录下找到$Proxy0.class文件,这个就是生成的代理类的字节码。反编译出来看下

package com.sun.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;import org.zheng.proxy.jdk.IDBQuery;public final class $Proxy0 extends Proxy  implements IDBQuery{  private static Method m1;  private static Method m3;  private static Method m2;  private static Method m0;  public $Proxy0(InvocationHandler paramInvocationHandler)    throws   {    super(paramInvocationHandler);  }  public final boolean equals(Object paramObject)    throws   {    try    {      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();    }    catch (RuntimeException localRuntimeException)    {      throw localRuntimeException;    }    catch (Throwable localThrowable)    {    }    throw new UndeclaredThrowableException(localThrowable);  }  public final String getElement(String paramString)    throws   {    try    {      return (String)this.h.invoke(this, m3, new Object[] { paramString });    }    catch (RuntimeException localRuntimeException)    {      throw localRuntimeException;    }    catch (Throwable localThrowable)    {    }    throw new UndeclaredThrowableException(localThrowable);  }  public final String toString()    throws   {    try    {      return (String)this.h.invoke(this, m2, null);    }    catch (RuntimeException localRuntimeException)    {      throw localRuntimeException;    }    catch (Throwable localThrowable)    {    }    throw new UndeclaredThrowableException(localThrowable);  }  public final int hashCode()    throws   {    try    {      return ((Integer)this.h.invoke(this, m0, null)).intValue();    }    catch (RuntimeException localRuntimeException)    {      throw localRuntimeException;    }    catch (Throwable localThrowable)    {    }    throw new UndeclaredThrowableException(localThrowable);  }  static  {    try    {      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });      m3 = Class.forName("org.zheng.proxy.jdk.IDBQuery").getMethod("getElement", new Class[] { Class.forName("java.lang.String") });      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);      return;    }    catch (NoSuchMethodException localNoSuchMethodException)    {      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());    }    catch (ClassNotFoundException localClassNotFoundException)    {    }    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());  }}

可以看到:

1)$Proxy0继承了Proxy实现了IDBQuery接口。

2)getElement方法,是this.h.invoke(this, m3, new Object[] { paramString }); 这就是调用我们DBQueryProxy的invoke方法,传入代理类本身的实例,试想,如果还是用method.invoke(o,args)那么就一直是跳入代理类执行了。

3)这个代理类本身是没有真实对象的,所以必须要依赖于外部传入的对象来执行方法。所以在DBQueryProxy需要依赖真实对象,就是这么回事。


好的,理清楚了,下篇写CGLib动态代理的。

0 0