JDK动态代理

JDK动态代理

使用JDK提供的Proxy创建动态代理类,我们需要先定义一个接口,并创建接口的实现类,然后创建InvocationHandler的实现类,再调用Proxy的newProxyInstance方法创建代理类。newProxyInstance方法的定义如下。

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

newProxyInstance方法的各参数说明:

◆loader: 用于加载JDK生成的代理类的类加载器;

◆interfaces:代理类实现的接口,即被代理的对象实现的接口,不同于继承,一个类可以实现多个接口,因此interfaces参数是一个Class类型的数组;

◆h:每个代理类的方法调用处理程序都必须实现InvocationHandler接口,当我们通过代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口的实例的invoke方法来调用。

InvocationHandler是方法调用处理器,或者说是方法拦截器。InvocationHandler接口只有一个invoke方法,方法的定义如下。

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

InvocationHandler的invoke方法各参数说明:

◆proxy:代理对象,即JDK创建的动态代理类的实例,这个参数一般用不到;

◆method:调用的方法,method是通过反射拿到的;

◆args:调用方法传递的参数;

我们可通过配置系统参数将JDK生成的动态代理类输出到class文件,class文件会输出在项目根目录下的com/sun/proxy目录下。配置代码如下。

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

我们以打印第三方API调用耗时为例,将发送http请求封装为一个模版类,通过代理模版类统计每个方法的执行耗时。

先定义Http请求模版接口HttpRequestTemplate,如代码清单6-1所示。

代码清单6-1 HttpRequestTemplate接口

public interface HttpRequestTemplate {
    HttpResponse doGet(HttpRequest httpRequest);
    HttpResponse doPost(HttpRequest httpRequest);
}

创建HttpRequestTemplate接口的实现类HttpRequestTemplateImpl,如代码清单6-2所示。

代码清单6-2 HttpRequestTemplateImpl类

public class HttpRequestTemplateImpl implements HttpRequestTemplate {

    @Override
public HttpResponse doGet(HttpRequest httpRequest) {
try {
       Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
        return new HttpResponse();
    }

    @Override
public HttpResponse doPost(HttpRequest httpRequest) {
try {
       Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
        return new HttpResponse();
    }

}

创建一个InvocationHandler的实现类,我们取名为HttpRequestInvocationHandler,如代码清单6-3所示。

代码清单6-3 HttpRequestInvocationHandler类

public class HttpRequestInvocationHandler implements InvocationHandler {

    private HttpRequestTemplate target;

    public HttpRequestInvocationHandler(HttpRequestTemplate target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startMs = System.currentTimeMillis();
        try {
            return method.invoke(target, args);
        } finally {
            long cntMs = System.currentTimeMillis() - startMs;
            System.out.println(method.getName() + "方法的执行耗时为" + cntMs + "毫秒");
        }
    }

}

HttpRequestInvocationHandler的构造方法要求传递一个实现HttpRequestTemplate接口的对象,这个对象就是被代理的目标对象。当调用JDK生成的HttpRequestTemplate接口的动态代理对象的方法时,通过反射调用构造方法传入的目标对象的方法。我们在方法执行前后加入了耗时统计。

现在我们调用Proxy的newProxyInstance方法创建HttpRequestTemplate接口的动态代理对象,如代码清单6-4所示。

代码清单6-4 创建HttpRequestTemplate接口的动态代理对象

public static void main(String[] args) {
        HttpRequestTemplate target = new HttpRequestTemplateImpl();
        // 创建代理对象
HttpRequestTemplate requestTemplate = (HttpRequestTemplate)
                Proxy.newProxyInstance(JdkAop.class.getClassLoader(),
                        new Class[]{HttpRequestTemplate.class},
                        new HttpRequestInvocationHandler(target));
}

JDK生成的HttpRequestTemplate接口的动态代理类,如代码清单6-5所示。

代码清单6-5 JDK生成的HttpRequestTemplate接口的动态代理类

 public final class $Proxy0 extends Proxy implements HttpRequestTemplate {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    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 HttpResponse doGet(HttpRequest var1) throws  {
        try {
            return (HttpResponse)super.h.invoke(this, m3, 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 HttpResponse doPost(HttpRequest var1) throws  {
        try {
            return (HttpResponse)super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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.wujiuye.asmbytecode.book.sixth.HttpRequestTemplate").getMethod("doGet", Class.forName("com.wujiuye.asmbytecode.book.sixth.HttpRequest"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.wujiuye.asmbytecode.book.sixth.HttpRequestTemplate").getMethod("doPost", Class.forName("com.wujiuye.asmbytecode.book.sixth.HttpRequest"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从代码清单6-5中可以看出,JDK生成的代理类继承了Proxy类,并实现HttpRequestTemplate接口。Proxy类要求构造方法必须传入一个InvocationHandler,在代理类实现的接口方法中都会调用这个InvocationHandler的invoke方法。JDK实现的代理类还代理了equals、toString、hashCode方法。

调用InvocationHandler的invoke方法所需的三个参数,分别是代理对象的引用、反射获取的接口方法的Method对象、方法参数包装成的一个数组对象。JDK为代理类生成一个静态代码块,在静态代码块中通过反射取得接口方法的Method对象。