JDK动态代理源码分析


JDK动态代理的使用

接口类

// 被代理的接口
public interface Subject {
    void request() throws Exception;
}

接口的实现类

public class SubjectImpl implements Subject{
    @Override
    public void request() throws Exception {
        System.out.println("SubjectImpl execute request()");
    }
}

自定义Handler实现

public class JDKProxySubject implements InvocationHandler {
    private SubjectImpl realSubject;

    public JDKProxySubject(SubjectImpl realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result = null;
        try {
            result = method.invoke(realSubject,args);
        } catch (Exception e) {
            throw e;
        } finally {
            System.out.println("after");
        }

        return result;
    }
}

其中,invoke方法的参数有值得说道的地方。

method是我们要调用的方法,args是方法参数,proxy呢?在方法里面用的是构造函数传进来的实现类,并没有用到proxy,原因何在?

  • 可以反射获取代理对象的信息
  • 可以将代理对象返回以进行连续调用

看例子:

// 被代理的接口
public interface Subject {
    Subject request() throws Exception; //返回自身,以便连续调用
}

public class SubjectImpl implements Subject{
    @Override
    public Subject request() throws Exception {
        System.out.println("SubjectImpl execute request()");
        return this;
    }
}

// 调用
public class Main {
    public static void main(String[] args) throws Exception {
        Subject subject = (Subject) Proxy.newProxyInstance(
                Main.class.getClassLoader(),
                new Class[]{Subject.class},
                new JDKProxySubject(new SubjectImpl())
        );

        //连续调用
        subject.request().request();
    }
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("before");
    Object result = null;
    try {
        result = method.invoke(realSubject,args);
    } catch (Exception e) {
        throw e;
    } finally {
        System.out.println("after");
    }
    //1. 获取代理类对象信息
    //com.sun.proxy.$Proxy0
    System.out.println(proxy.getClass().getName());


    return result; //返回的并不是被代理类,而是代理类。
    //结果:
    //before
    //SubjectImpl execute request()
    //after
    //com.sun.proxy.$Proxy0
    //SubjectImpl execute request()

    //2. 连续调用,返回的是真正的代理类
    //这个才是真正调用了两次代理类
    //return proxy;
    //结果:
    //before
    //SubjectImpl execute request()
    //after
    //com.sun.proxy.$Proxy0
    //before
    //SubjectImpl execute request()
    //after
    //com.sun.proxy.$Proxy0
}

至此,使用方法比较清晰了,下面是其原理。

JDK动态代理源码分析

使用第一步:

Subject subject = (Subject) Proxy.newProxyInstance(
                Main.class.getClassLoader(),
                new Class[]{Subject.class},
                new JDKProxySubject(new SubjectImpl())
        );
subject.request();

获取代理对象是通过Proxy.newProxyInstance,这是一个静态方法,有三个参数:

  • ClassLoader
  • 接口类数组
  • handler
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    // handler判空
    Objects.requireNonNull(h);
    // 接口类复制一份,之后操作不会涉及原数组
    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    //生成代理类
    Class<?> cl = getProxyClass0(loader, intfs);
    // 生成的类继承了Proxy,Proxy部分代码
    // protected InvocationHandler h;
    // protected Proxy(InvocationHandler h) {
    //    Objects.requireNonNull(h);
    //    this.h = h;
    //}

    //调用以handler为参数的构造方法
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        
        //private static final Class<?>[] constructorParams = { InvocationHandler.class }
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        //上面的构造函数就是获取了前面的Proxy构造方法,将handler传给Proxy
        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});
        //最终返回以handler为参数的构造函数构造出的对象
    } catch (Exception e) {
        //...
    }
}

可以通过设置参数保存代理类:

//通过设置参数,将生成的代理类的.class文件保存在本地
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

查看生成的代理类:

package com.sun.proxy;

import JDKProxy.Subject;
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 Subject {
    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 Subject request() throws Exception {
        try {
            return (Subject)super.h.invoke(this, m3, (Object[])null);
        } catch (Exception | 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("JDKProxy.Subject").getMethod("request");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

就比较明显了,JDK动态代理是生成了一个新的类A,A继承Proxy并且实现了指定接口数组的方法。A的构造方法传入一个参数handler,在调用具体方法的时候,如我们自定义的handler:

public final Subject request() throws Exception {
    // 先忽视try catch, m3是request的method,空参数
    return (Subject)super.h.invoke(this, m3, (Object[])null);
}

其中有一点,这里传入的是this,也就是代理类,即我们之前说的handler的第一个参数。

生成代理类用到的方法为:

//生成代理类
Class<?> cl = getProxyClass0(loader, intfs);

这里就是确定包名、类名、接口这些信息来拼接生成代理类,根据生成的类来看也比较清晰,指定默认方法和自定义方法的method后,都去调用handler的invoke方法。

其中对大小进行了限制,还有有缓存就用缓存。

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    return proxyClassCache.get(loader, interfaces);
}

理解到这种程度应该差不多了。

总结

还有关于JDK动态代理只能代理有接口的对象这一点,看到一个挺有意思的解释,记录一下:

public class Proxy implements java.io.Serializable {
    ...
}

有一个说法是:Java是单继承,继承了Proxy之后,就无法再继承其他类了,如果要代理没有接口的对象,就要继承被代理的对象,这违背了单继承原则。

对应反驳的说法说:Proxy类没有继承别的类(先无视Object),完全可以不继承Proxy,而是采用例如拼接在一起等等方法去掉这个继承。代理接口应该是设计的思想(你个类就应该有个接口类才合适),根据这个思想采用继承Proxy方式实现的代理,这样需要修改代码的时候,只要修改handler就可以了。接口是相对稳定的,方法可能有多种不同的实现,handler本身就是自定义的,改就改了,而生产代理对象的代码可以保持不变。

InvocationHandler handler = new JDKProxySubject(new SubjectImpl());

Subject subject = (Subject) Proxy.newProxyInstance(
    Main.class.getClassLoader(),
    new Class[]{Subject.class},
    handler
);

不过不管怎么说,JDK动态代理是不适应无接口类了,对应的解决方法又CGLIB动态代理。

参考文献

解析JDK动态代理实现原理

java InvocationHandler invoke方法的第一个参数有什么用?

InvocationHandler中invoke方法中的第一个参数proxy的用途