代理模式理解起来还是比较简单的。大致可以理解为代理人与被代理人实现同一接口定义的行为,在外部调用者调用定义的行为时,不直接调用代理人的提供的行为,而是调用代理人提供的行为,此时调用者是不会感觉到区别的,因为代理人有着和被代理人的行为定义是相同的,调用者感觉不到区别(也就是实现了同一接口),在代理人的行为中在去调用被代理人的行为,并且可以实现增强。记得前几年有部电影,我觉得其实就很好的诠释了”代理”这个概念,想必看了就明白代理模式是怎么回事了。
更多的不再细说了,这不是本文的重点。我们这次主要是学习动态代理。
上边说的代理模式实际上细分的话,可以分为静态代理和动态代理,在实际编码中,代理模式一般是以动态代理的形式出现的。
动态代理的实现途径有很多种,比如用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 定义的处理流程,这个处理流程中我们回去调用默认实现,并提供增强。