代理模式理解起来还是比较简单的。大致可以理解为代理人与被代理人实现同一接口定义的行为,在外部调用者调用定义的行为时,不直接调用代理人的提供的行为,而是调用代理人提供的行为,此时调用者是不会感觉到区别的,因为代理人有着和被代理人的行为定义是相同的,调用者感觉不到区别(也就是实现了同一接口),在代理人的行为中在去调用被代理人的行为,并且可以实现增强。记得前几年有部电影,我觉得其实就很好的诠释了”代理”这个概念,想必看了就明白代理模式是怎么回事了。
更多的不再细说了,这不是本文的重点。我们这次主要是学习动态代理。
上边说的代理模式实际上细分的话,可以分为静态代理和动态代理,在实际编码中,代理模式一般是以动态代理的形式出现的。
动态代理的实现途径有很多种,比如用JDK动态代理和CGLIB动态代理,本篇文章我们主要关注JDK动态代理。
首先了解一下怎样使用。来看一个具体的例子吧:
package com.github.since1986.learn.java.proxy; //定义好行为 public interface InterfaceA { String doSomething(String param); }
//定义好默认实现 package com.github.since1986.learn.java.proxy; public class ImplementationA implements InterfaceA { @Override public String doSomething(String param) { return String.format("hello, %s.", param); } }
package com.github.since1986.learn.java.proxy.dynamic; import com.github.since1986.learn.java.proxy.ImplementationA; import com.github.since1986.learn.java.proxy.InterfaceA; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class DynamicProxyCase { public static void main(String[] args) { //生成代理 InterfaceA proxyA = (InterfaceA) Proxy.newProxyInstance(DynamicProxyCase.class.getClassLoader(), new Class[]{InterfaceA.class}, new InvocationHandler() { //反射调用默认行为(并可在此实现增强) @Override public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return method.invoke(new ImplementationA(), args) + " Bye~"; } }); //调用接口定义的行为 System.out.println(proxyA.doSomething("Test")); } }
运行结果:
hello, Test Bye~.
总结起来,写JDK动态代理一共4步(比把大象装到冰箱里多一步):
实现一个回调方法(在这个回调中调用第2步中的默认行为)
java.lang.reflect.InvocationHandler#invoke(Object proxy, Method method, Object[] args) throws Throwable;
调用 Proxy#newProxyInstance
生成代理实例
java.lang.reflect.Proxy#newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
了解了怎样使用,我们再来看看JDK动态代理是怎么实现的吧。
先看
java.lang.reflect.Proxy#newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
这个方法,这个方法是整个动态代理流程的入口点。
在这个方法中会去反射获得我们定义的接口的 Class
/* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); //intfs是克隆的参数传进来的interfaces:Class<?>[] intfs = interfaces.clone();
然后通过这个Class拿到 java.lang.reflect.Constructor
final Constructor<?> cons = cl.getConstructor(constructorParams);
再通过 java.lang.reflect.Constructor
的 java.lang.reflect.Constructor#newInstance
来创建代理的实例并返回,所以我们调用 java.lang.reflect.Proxy#newProxyInstance
返回的代理实例其实是 java.lang.reflect.Constructor#newInstance
提供的。
接下来再来看 java.lang.reflect.Constructor#newInstance
:
ConstructorAccessor ca = constructorAccessor; // read volatile if (ca == null) { ca = acquireConstructorAccessor(); } @SuppressWarnings("unchecked") T inst = (T) ca.newInstance(initargs); return inst;
注意这一段, java.lang.reflect.Constructor#newInstance
返回的实际上是 sun.reflect.ConstructorAccessor#newInstance
所返回的
我们再转到 sun.reflect.ConstructorAccessor#newInstance
,可以看到这个方法是个接口方法
我们打开它其中一个实现 sun.reflect.NativeConstructorAccessorImpl
,可以看到再往下就是开始操作 class
文件结构了,我们到此基本上大致了解一部分JDK动态代理的原理了,那就是生成 class
文件。那么我们如何看到生成的 class
文件呢?另外前边提到的 InvocationHandler
在流程中没有出现啊?不用着急,我们一一来。
怎样看到生成的 class
文件呢?在生成动态代理前设置一个 Properties
就可以了(这个 Properties
源码位置在 sun.misc.ProxyGenerator#saveGeneratedFiles
): System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
。设置好后,我们可以看到生成的以 $
开头的 class
文件:
用idea打开这个 class
文件看反编译后的代码:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import com.github.since1986.learn.java.proxy.InterfaceA; 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 InterfaceA { //动态生成的代理类,实现了我们定义的接口 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 String doSomething(String var1) throws { //我们定义的行为(接口方法) try { return (String)super.h.invoke(this, m3, 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")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.github.since1986.learn.java.proxy.InterfaceA").getMethod("doSomething", Class.forName("java.lang.String")); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
注意上边代码 doSomething
方法中这一段:
return (String)super.h.invoke(this, m3, new Object[]{var1});
这里面的 super.h
是 java.lang.reflect.Proxy#h
这个属性:
/** * the invocation handler for this proxy instance. * @serial */ protected InvocationHandler h;
也就是说 InvocationHandler
是被插入到了生成的 class
文件中的。
我么继续看 h.invoke(this, m3, new Object[]{var1});
,在这个调用中传入的参数,第一个 this
就是当前生成的 $Proxy0
的实例,第二个参数 m3
实际上就是我们定义的接口方法
m3 = Class.forName("com.github.since1986.learn.java.proxy.InterfaceA").getMethod("doSomething", Class.forName("java.lang.String"));
第三个参数是被传入的被调用的接口方法的实际参数。这个 h.invoke
就是前面我们提到的那个 java.lang.reflect.InvocationHandler
的方法:
/** * Processes a method invocation on a proxy instance and returns * the result. This method will be invoked on an invocation handler * when a method is invoked on a proxy instance that it is * associated with. * * @param proxy the proxy instance that the method was invoked on * * @param method the {@code Method} instance corresponding to * the interface method invoked on the proxy instance. The declaring * class of the {@code Method} object will be the interface that * the method was declared in, which may be a superinterface of the * proxy interface that the proxy class inherits the method through. * * @param args an array of objects containing the values of the * arguments passed in the method invocation on the proxy instance, * or {@code null} if interface method takes no arguments. * Arguments of primitive types are wrapped in instances of the * appropriate primitive wrapper class, such as * {@code java.lang.Integer} or {@code java.lang.Boolean}. * * @return the value to return from the method invocation on the * proxy instance. If the declared return type of the interface * method is a primitive type, then the value returned by * this method must be an instance of the corresponding primitive * wrapper class; otherwise, it must be a type assignable to the * declared return type. If the value returned by this method is * {@code null} and the interface method's return type is * primitive, then a {@code NullPointerException} will be * thrown by the method invocation on the proxy instance. If the * value returned by this method is otherwise not compatible with * the interface method's declared return type as described above, * a {@code ClassCastException} will be thrown by the method * invocation on the proxy instance. * * @throws Throwable the exception to throw from the method * invocation on the proxy instance. The exception's type must be * assignable either to any of the exception types declared in the * {@code throws} clause of the interface method or to the * unchecked exception types {@code java.lang.RuntimeException} * or {@code java.lang.Error}. If a checked exception is * thrown by this method that is not assignable to any of the * exception types declared in the {@code throws} clause of * the interface method, then an * {@link UndeclaredThrowableException} containing the * exception that was thrown by this method will be thrown by the * method invocation on the proxy instance. * * @see UndeclaredThrowableException */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
就是在这个方法中,我们调用了实际的默认实现,并实现了对默认实现的增强:
//反射调用默认行为(并可在此实现增强) @Override public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return method.invoke(new ImplementationA(), args) + " Bye~"; }
可以看到实际上这个方法中的3个参数 Object proxy, Method method, Object[] args
就是 $Proxy0
通过 h.invoke(this, m3, new Object[]{var1});
传递给我们的,也就是说,我们实现的 InvocationHandler
的 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
实际上是一个 回调
,也就是我们预先定义好的,然后JDK生成的类 $Proxy0
回过来调用的。
到这里,整个流程基本上就清楚了,总结一下:JDK动态代理的基本原理就是 我们定义好接口和默认实现,JDK根据通过生成 class
文件的方式”动态”的生成一个代理类,这个代理类实现了我们定义的接口,并在接口实现方法中回调了我们通过 InvocationHandler
定义的处理流程,这个处理流程中我们回去调用默认实现,并提供增强。