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对象。