JDK动态代理原理

本文主要讲解下JDK动态代理的实现原理,其基本使用如下:

// 实例化自定义调用处理器实例
InvocationHandler handler = new MyInvocationHandler(...);
// 获取代理对象方式一 
Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
                newInstance(handler);
// 获取代理对象方式二
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                           new Class<?>[] { Foo.class },
                           handler);

可以看出,只要给出要实现类的加载器、接口列表就能够在运行时创建出代理类。代理类的实例化需要传入自定义的调用处理器实例,具体代理的内容均在处理器实例中体现。
JDK动态代理在使用上十分简单,但内部具体实现逻辑有很多细节指的我们探索。下文将先从目标代理类入手,分析其结构和调用关系。然后分析Proxy中探索生成代理类的过程。

一、代理类

1.1 获取代理类

代理类是运行时生成的,一般不会产生具体的类文件。如果想要获取代理类,可设置如下系统参数:

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

不同JDK版本可能参数会有所不同,参考ProxyGenerator#saveGeneratedFiles指定的Key值。
该值设置为true后,程序生成代理类是就会将对应的类文件路径为:{项目根目录}/com/sun/proxy/$proxy{index}.class,其中index为代理类的唯一序号。,
下面是为简单的Person接口生成的代理类:

public interface Person {
    void ageNum();
}
public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    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 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 void ageNum() throws  {
        try {
            super.h.invoke(this, m3, (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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.design.代理模式.jdkDynamic.Person").getMethod("ageNum");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到代理类使用final修饰不允许被继承,访问权限是否为public由其继承的接口Person的权限决定。
代理类继承了Proxy类,并实现了指定接口的所有方法。是否正是因为代理类需继承Proxy类,且Java不允许多继承,所以代理类就只能传入对应的接口呢?为什么代理类必须得继承Proxy类?

1.2 构造方法

代理类仅提供了一个构造方法,并且需传入InvocationHandler实例。InvocationHandler就是具体方法调用时的代理处理器。这个后续会说到,先有个印象。【InvocationHandler实例必传】

1.3 类成员变量

代理类中的成员变量均为静态的方法句柄(java.lang.reflect.Method),代理类在加载时,会通过反射的方法初始化各个Method。

Calss.forName(String className).getMethod(String methodName, ...parameterTypes)

1.4 类方法

代理类会实现接口的多个方法,并且都使用final关键字修饰,不允许被重写(本来代理类也不能被继承),访问权限依据接口中方法的访问权限决定。
我们从上面例子可以看到,代理类不仅实现了接口的方法,同时也实现了Object类hashCode()、equals()、toString()。这就说明了代理类不仅会代理指定接口的方法,默认也会代理Object类中公开的、非本地的方法。

try {
	// super.h 就是 代理类构造方法传入的InvocationHandler实例
    return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
    throw var3;
} catch (Throwable var4) {
    throw new UndeclaredThrowableException(var4);
}

在方法内部执行逻辑,我们可以发现,代理类执行某个方法时,会调用InvocationHandler实例的invoke方法。

二、调用处理器

我们在前面代理类的分析中发现,代理类执行任何一个方法,实际上都会通过InvocationHandler实例的invoke方法进行处理。每一个代理实例都必须关联一个InvocationHandler实例。

2.1 InvocationHandler接口

InvocationHandler是java.lang.reflect包下提供的接口,该接口仅声明了一个方法:

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

任何代理类调用方法时,都会调用其关联的InvocationHandler实例的invoke方法获取方法返回结果。对于其方法参数说明如下:

  • Object proxy:被调用的代理类实例。
  • Method method:代理类实例分派的对应方法句柄。
  • Object[] args:方法调用的参数列表
    因此,代理类调用的任何方法最终都会通过这个方法实际执行。调用方可以根据自己的需要通过实现此方法达到自己的目的。

三、核心类-Proxy

Proxy是JDK动态代理的核心类,主要负责代理类的生成、实例化任务。

public class Proxy implements java.io.Serializable {
}

从类声明上看,Proxy实现Serializable接口表明代理类(继承Proxy)是允许被序列化&反序列化的。
Proxy类的访问权限为public且没有声明final关键字。即便如此,一般情况下也没有人手动继承Proxy类,因为Proxy要么是静态方法,要么是私有方法,意即,单独继承Proxy几乎没有意义。

3.1 构造方法

Proxy提供了两个构造方法。

private Proxy() {
}

protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    this.h = h;
}

Proxy的无参构造的访问权限为私有,这就意味着Proxy是不允许用户直接实例化的。实际上,Proxy直接实例化也没有意义,公开方法均为静态方法。
Proxy的有参构造访问权限为protected,是提供给子类用的,比如生成的代理类是Proxy的子类,当代理类实例化时,就会通过这个构造方法将调用处理器实例存储在Proxy#属性中。

3.2 属性

Proxy类中有三个属性,其中2个私有类型,1个受保护类型。

  • Class<?>[] constructorParams:私有属性,在实例化代理类时,会调用参数列表为constructorParams的构造器进行初始化。默认情况下,constructorParams为仅包含InvocationHandler.class的数组。
  • WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache:私有属性,在生成代理类时,会先通过WeakCache去生产代理类并缓存起来。如何生成代理类与WeakCache息息相关,此处建议先看下WeakCache相关文章。【WeakCache二级缓存
    WeakCache是个二级缓存,一级Key由类加载器生成,二级Key由接口列表生成,Value值就是代理类class。proxyClassCache会在Proxy加载时就初始化,初始化传入的KeyFactory用于结合接口列表产生二级Key,ProxyClassFactory会根据类加载器、接口列表来产生具体的代理类class对象。因此,ProxyClassFactory将会是Proxy的重头戏。
  • InvocationHandler h:protected权限,在代理类实例化时赋值InvocationHandler 实例,代理类执行任何方法都会分派给InvocationHandler实例的invoke方法执行。

3.4 内部类

内部类的内容是Proxy原理的重点,阅读本节之前必须先看懂【WeakCache二级缓存

Proxy类中定义了5个私有静态内部类,并且均使用final关键字修饰。

  1. KeyFactory类实现了函数式接口,根据代理接口列表生成二级缓存的sub-Key。
  2. Key1、Key2、KeyX:这三个类是KeyFactory中根据接口个数选择的不同Sub-Key类型,目的是优化性能。
  3. ProxyClassFactory类也实现了函数式接口,根据类加载器、接口列表生成代理类class。

3.4.1 KeyFactory、KeyX

KeyFactory实例作为WeakCache实例化时的第一个工厂函数对象subKeyFactory,主要用于代理类缓存的二级Key,即sub-Key的生成

private static final class KeyFactory implements BiFunction<ClassLoader, Class<?>[], Object> {
    @Override
    public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
        switch (interfaces.length) {
            case 1: return new Key1(interfaces[0]); // the most frequent
            case 2: return new Key2(interfaces[0], interfaces[1]);
            case 0: return key0;
            default: return new KeyX(interfaces);
        }
    }
}

KeyFactory类实现了BiFunction的apply方法。BiFunction(Binary Function)相比于Function接口,BiFunction会根据两个参数返回结果的函数式接口。KeyFactory其实就对应着WeakCache的subKeyFactory,因此通过KeyFactory的泛型参数可以看出代理类缓存的一级Key就是ClassLoader类型的类加载器,生成二级Key的另外一个参数就是Class<?>[]类型的类信息数组(实际上,这里其实是接口类信息数组,后面会说明)。
KeyFactory#apply方法生成subKey需要传入两个参数:类加载器及接口信息列表。而在实际逻辑中,可以看出KeyFactory在生成subKey的时候并没有考虑类加载器。因此代理类缓存的结构可记为<类加载器,接口信息,代理类>,也就意味着代理类在同一类加载器下同样的接口信息下是唯一的
那具体是如何根据接口类信息数组生成二级Key呢?从KeyFactory#apply方法可以看到,二级Key实际上就是Proxy提供内部类Key[1,2,X]对象。这里是根据接口的长度来返回不同类型的对象。

这里有两个问题暂时还没搞懂:

  1. Key1、Key2、KeyX的区别在哪里?内存占用和性能上似乎没有区别,都是用KeyX不行吗?
  2. KeyX为什么不继承弱引用呢?虽然subKey即便不是弱引用也无所谓,WeakCache都没要求。
    KeyX对象只会被用于WeakCache

3.4.2 ProxyClassFactory

ProxyClassFactory实例作为WeakCache实例化时的第二个工厂函数对象valueFactory,主要用于生成代理类Class

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
   ...
}

ProxyClassFactory同样也是实现了BiFunction接口,实现的apply方法会根据类加载器和类信息数组生成代理类Class并返回。
先来看ProxyClassFactory内部类的两个私有类成员变量

// 所有代理类名称前缀
 private static final String proxyClassNamePrefix = "$Proxy";
 // 下一个要生成的代理类序号
 private static final AtomicLong nextUniqueNumber = new AtomicLong();

这两个成员变量就是用于拼接生成代理类名的,即proxyClassNamePrefix + nextUniqueNumber,比如第一章节给出的“$Proxy0”就是生成的第一个代理类名。
ProxyClassFactory的核心还是用于生成代理类Class的apply方法,代码及注释:

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
	// 1. 第一部分是类信息数组的校验和验证部分
   	Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
    for (Class<?> intf : interfaces) {
        // 1.1 验证所有类信息都能够通过当前类加载器加载 
        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");
        }
        // 1.2 验证所有类信息均为接口类型【JDK动态代理只能代理接口】 
        if (!interfaceClass.isInterface()) {
            throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");
        }
        // 1.3 验证接口信息数组不存在重复
        if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
            throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());
        }
    }

    String proxyPkg = null;     // 定义代理类所在的包路径
    int accessFlags = Modifier.PUBLIC | Modifier.FINAL;		// 默认代理类为public 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.
     */
    // 2. 如果接口数组中存在非public,那代理类也是非public权限。
    // 	并且,代理类所在的包路径为非public接口所在路径【多个非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) {
        // 若接口均为public权限,默认代理类包路径为com.sun.proxy
        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
    }

    /*
     * Choose a name for the proxy class to generate.
     */
     // 3. 拼接出代理类的全限定名
    long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg + proxyClassNamePrefix + num;

    /*
     * 4. 根据代理类名、实现的接口、代理类权限信息生成代理类字节数组
     */
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces, accessFlags);
    try {
    	// 调用本地方法,使用指定的类加载器加载代理类Class对象
        return defineClass0(loader, proxyName,
                            proxyClassFile, 0, proxyClassFile.length);
    } catch (ClassFormatError e) {
        throw new IllegalArgumentException(e.toString());
    }
}

代码十分简单,几乎就是验证了一些代理类信息正确性,确定代理类包路径、类名、访问权限等信息,最重要的是通过ProxyGenerator的静态方法来生成代理类字节码数组,并调用本地方法加载并返回代理类Class对象。

3.3 公开方法

Proxy类提供了四个静态公开方法。

  1. Class<?> getProxyClass(ClassLoader loader, Class<?>… interfaces)
    根据类加载器、代理类需实现的接口数组返回代理类对象。内部逻辑很简单,几乎就是通过Proxy#proxyClassCache缓存来获取-可能是从已有缓存中获取也有可能需要通过ProxyClassFactory的apply方法生成代理对象。【逻辑简单,本文不再详细叙述,读者在之前的基础上阅读即可】
  2. boolean isProxyClass(Class<?> cl)
    判断传入的Class对象是否为为代理类对象。
    返回true的代理类条件:Proxy子类,且存在于proxyClassCache缓存中。【问:该缓存失效怎么办?代理类缓存一定是GC失效吗?】
  3. InvocationHandler getInvocationHandler(Object proxy)
    返回传入代理类对象的InvocationHandler对象(当前proxy对象的Class必须为代理类对象)。
  4. Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    根据创建代理类对象所有必须参数,返回最终代理类对象实例。

四个方法内部逻辑均十分简单清晰,这里仅给出常用newProxyInstance方法代码及注释。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
    throws IllegalArgumentException
{
	// 调用处理器必须不能为null
    Objects.requireNonNull(h);

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

    /*
     * 根据类加载器、要实现的接口列表获取或加载代理类Class对象
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
			
     	// 反射找到代理类的构造方法,包含调用处理器参数
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {	// 如果当前代理类非public【接口存在非public】
            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);
    }
}

可以看到,如果前面一大部分能够理解的话,Proxy提供的四个公开静态方法实际上理解起来十分简单,这里也不做过多赘述【重点还是二级缓存&代理类的生成】。另外,还有个细节就是所有的公开方法均使用了@CallerSensitive注解,深入了解可参考@CallerSensitive一些理解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值