如下 JDK 源码及 Javadoc 均从 java version "1.8.0_152" 版本实现中摘录或翻译java
代理模式即 Proxy Pattern,23 种经常使用的面向对象软件的设计模式之一。代理模式为其余对象提供一种代理以控制对这个对象的访问。在某些状况下,一个对象不适合或者不能直接引用另外一个对象,而代理对象能够在客户端和目标对象之间起到中介的做用。代理模式由抽象角色、代理角色、真实角色组成。抽象角色经过接口或抽象类声明真实角色实现的业务方法;代理角色实现抽象角色,是真实角色的代理,经过真实角色的业务逻辑方法来实现抽象方法,并能够附加本身的操做;真实角色实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。详见代理模式类图: 编程
动态代理即动态的代理模式,所谓动态,是指抽象类(即抽象角色)在编译期是未肯定的,在运行期生成。相对的,静态代理中抽象类的行为是在编译期肯定的。动态代理是 AOP(面向切面编程)常见的实现方式。设计模式
Java 的动态代理使用起来特别简单,只须要咱们掌握 Proxy.newProxyInstance
方法便可。 Proxy.newProxyInstance 方法在 JDK 中定义以下:数组
/** * 返回一个受调用处理器 (InvocationHandler) 管理,实现了指定接口的代理类的实例 * * @param loader 声明这个代理类的 ClassLoader * @param interfaces 代理类实现的接口列表 * @param h 处理代理类的调用的调用处理器 * @return 一个受调用处理器 (InvocationHandler) 管理,实现了指定接口的代理类的实例 * @throws IllegalArgumentException 违反了 getProxyClass 函数的参数限制条件 * @throws SecurityException 若是安全管理器存在而且下面的任意条件知足: * (1) 传入的 loader 是 null 且调用者的类加载器非空, * 使用 RuntimePermission("getClassLoader")权限 * 调用 SecurityManager#checkPermission禁止访问 * * (2) 对于每个代理接口,调用者的类加载器与接口类加载器不一样或不是其父类, * 而且调用 SecurityManager#checkPackageAccess 无权访问接口 * * (3) 全部传入的代理接口都是非公共的,且调用者类与非公共接口不在同一个包下, * 使用 ReflectPermission("newProxyInPackage.{package name}") 调用 * SecurityManager#checkPermission 无访问权限 * @throws NullPointerException interfaces 数组参数或其中的元素为 null,以及调用处理器 h 为 null */
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException;
复制代码
从 Javadoc 中咱们能够获知,只须要传入相应的类加载器,接口,调用处理器便可产生一个代理实例,那么这里咱们不熟悉的就是 InvocationHandler 类,咱们看一下 InvocationHandler 类的代码:缓存
package java.lang.reflect;
/** * InvocationHandler是代理实例的调用处理器实现的接口。 * 每一个代理实例都有一个关联的调用处理器。 * 在调用代理实例的方法时,方法调用将被编码并分派给其调用处理程序的 invoke 方法。 * * @author Peter Jones * @see Proxy * @since 1.3 */
public interface InvocationHandler {
/** * 在代理实例上处理方法调用并返回结果。当在与其关联的代理实例上调用 * 方法时,将调用处理期上的此方法。 * * @param proxy 该方法被调用的代理实例 * * @param method Method 对象将是代理接口声明的方法,它多是代理 * 类继承方法的代理接口的超级接口。 * @param args 包含在代理实例的方法调用中传递的参数值的对象数组, * 若是interface方法不带参数,则为null。基本类型的参 * 数被封装在适当的基本封装类的实例中,好比 * java.lang.Integer 或者 java.lang.Boolean。 * @return 调用代理实例上的方法得到的返回值。若是接口方法的声明返 * 回类型是基本类型,则此方法返回的值必须是相应基本包装类 * 的实例;不然,它必须是转换为声明的返回类型的类型。若是 * 此方法返回的值为null,而且接口方法的返回类型为原始类型, * 则代理实例上的方法调用将引起NullPointerException。若是 * 此方法返回的值与上面所述的接口方法的声明返回类型不兼容, * 则将经过代理实例上的方法调用抛出ClassCastException。 * * @throws 抛出调用代理实例的方法时抛出的异常。异常的类型必须能够 * 转化为接口方法的 throws 子句中声明的异常类型,也能够分 * 配给不强制检查的异常类型 java.lang.RuntimeException 或 * java.lang.Error。若是这个方法抛出一个强制检查的异常, * 这个异常不能转化为接口方法的 throws 子句中声明的异常类 * 型,那么将会抛出包含这个异常的 * UndeclaredThrowableException 异常。 * * @see UndeclaredThrowableException */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
复制代码
从 Javadoc 中咱们知道,调用经过 Proxy.newProxyInstance
方法建立的代理实例中的方法时,会执行传入的 InvocationHandler#invoke
方法,代理实例中方法返回值为 InvocationHandler#invoke
方法返回值。
咱们作一个小测试:安全
package com.example.demo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
/** * 代理接口 */
interface ITest {
String test(String val);
}
/** * 代理实现类 */
class Test implements ITest {
@Override
public String test(String val) {
return val + "我是Test";
}
}
/** * 调用处理器 */
class TestInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
return args[0] + "我是TestProxy";
}
}
/** * 分别对正常实现的 ITest 实现类和动态代理实现类进行调用 */
void testProxy() {
ITest test = new Test();
ITest testProxy = (ITest) Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class[]{ITest.class}, new TestInvocationHandler());
System.out.println(test.test("Hello,"));
System.out.println("----------");
System.out.println(testProxy.test("Hello,"));
}
public static void main(String[] args) {
new Main().testProxy();
}
}
复制代码
输出结果为:bash
Hello,我是Test
----------
public abstract java.lang.String com.example.demo.Main$ITest.test(java.lang.String)
Hello,我是TestProxy
复制代码
从测试例子中,咱们能够看到两个特色:app
InvocationHandler#invoke
方法。不知各位使用 MyBatis 的时候有没有疑问,为何能够直接调用接口?答案就在这里,事实上,MyBatis 使用相似的技术,帮咱们实现了一个代理类,咱们拿到的都是接口的代理类实例。ide
为了突出重点,如下代码仅展现与主题相关的代码,防护性编程、异常处理等无关内容已被省略,完整实现请自寻 JDK函数
那么 Java 的动态代理是怎样实现的呢?这引发了个人好奇心。因此,咱们去看 JDK 源码。
查看 Proxy.newProxyInstance
的实现:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
final Class<?>[] intfs = interfaces.clone();
// 经过类加载器和接口使用 getProxyClass0 方法建立实现类
Class<?> cl = getProxyClass0(loader, intfs);
// 得到指定构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
// 建立实例
return cons.newInstance(new Object[]{h});
}
复制代码
其中两句建立实例的过程都是常见的反射操做,这里不赘述,值得咱们好奇的是 getProxyClass0
方法是如何经过接口建立类的?咱们继续跟进 getProxyClass0
方法的实现:
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
return proxyClassCache.get(loader, interfaces);
}
复制代码
咱们跟进至 proxyClassCache.get
的实现,这应该是一个负责缓存管理的类:
public V get(K key, P parameter) {
// Cache 置换、检查等实现均已省略,如下是 Cache 未命中时,建立新实现类的代码
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
V value = supplier.get();
return value;
}
复制代码
咱们跟进至 ProxyClassFactory#apply
的实现:
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
for (Class<?> intf : interfaces) {
interfaceClass = Class.forName(intf.getName(), false, loader);
// 对 interfaceClass 进行了系列权限检查,实现略
}
// 根据 interfaces、accessFlags 产生名为 proxyName 的代理类字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,
interfaces, accessFlags);
// 加载字节码,产生类对象
return defineClass0(loader, proxyName, proxyClassFile,
0, proxyClassFile.length);
}
复制代码
从代码中,咱们能够看到:
这里的 defineClass0 是一个 native 方法,咱们不深究。 ProxyGenerator.generateProxyClass 是对字节码进行操做,因为字节码的规范我不是很懂,因此此处,咱们去看他的代码实现对咱们帮助不大。那么怎么办呢?咱们作一个小实验:
public class Main2 {
/** * 代理接口 */
interface ITest {
String test(String val);
}
public static void main(String[] args) throws IOException {
// 经过 ProxyGenerator.generateProxyClass 产生字节码
byte[] testProxyBytes = ProxyGenerator.generateProxyClass("TestProxy", new Class[]{ITest.class});
// 将字节码输出到文件,而后咱们再反编译它,看看它的内容是什么
FileOutputStream fileOutputStream = new FileOutputStream("TestProxy.class");
fileOutputStream.write(testProxyBytes);
fileOutputStream.flush();
fileOutputStream.close();
}
}
复制代码
TestProxy.class 反编译后的源码:
public final class TestProxy extends Proxy implements ITest {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public TestProxy(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 test(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.example.demo.Main2$ITest").getMethod("test", 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());
}
}
}
复制代码
经过 ProxyGenerator.generateProxyClass
生成的类字节码有如下特色:
TestProxy(InvocationHandler var1)
传入调用处理器。super.h.invoke
并返回其结果。那么这里的 super.h
是什么呢,咱们看其父类 Proxy
的代码:
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
复制代码
恍然大悟!这里的 super.h
就是 TestProxy(InvocationHandler var1)
构造器中传入的 h。
Proxy.newProxyInstance
方法传入类加载器、接口类对象、调用处理器来建立代理类实例ProxyGenerator.generateProxyClass
方法根据传入接口类对象生成代理类的字节码,并加载字节码产生代理类对象