代理是一种结构型设计模式, 让你能提供真实服务对象的替代品给客户端使用。 代理接收客户端的请求并进行一些处理 (访问控制和缓存等), 然后再将请求传递给服务对象。
代理对象拥有和服务对象相同的接口, 这使得当其被传递给客户端时可与真实对象互换。
——Copy From refactoring.guru
一、静态代理
静态代理代码很简单,但其设计思想很重要。示例如下:
点击查看代码
1 | //UserService.java |
二、JDK动态代理
JDK动态代理可以看成一个“万能中介”,它最终实现方法粒度的增强,它的InvocationHandler
是“万能的”,根据我们传入的target以及调用不同的方法实现不同的增强效果。需要注意的是JDK动态代理只能代理接口,这与它背后的实现逻辑有关系。
2.1、为什么需要动态代理
静态代理虽然能够实现大多数功能,但每个目标对象都需要生成一个代理类,当被代理类太多时,开发和维护代理类将是一件很可怕的事情,同时静态代理还不具备处理动态生成的class的能力。基于上诉痛点,各种动态代理技术应运而生,JDK动态代理便是其中之一。
2.2、JDK动态代理示例
点击查看代码
1 | //接口&实现类参考上面静态代理的例子 |
我们可以看到,JDK动态代理核心就在于实现InvocationHandler
,重写invoke
方法。
2.3、InvocationHandler接口
InvocationHandler
可以理解为一个”执行器“,每个被代理的对象中的所有方法都要经过它,由它决定下一步怎么处理,所以实现InvocationHandler
是整个JDK动态代理的核心,不同的功能可以使用不同的InvocationHandler
来实现。InvocationHandler
中的核心方法invoke
:2.1、Object proxy: 该参数是 最终的目标代理对象,一般代理用不到它,在具有链式操作的代理对象中可以直接返回该对象,使得最终代理对象也可以使用链式操作。
2.2 、Method method:该参数是我们要调用的目标方法,一般会使用
method.invoke(target, args)
继续调用被代理对象的具体实现。2.3、Object[] args: 该参数是目标方法参数。
2.4、返回值:由于该方法是“万能的”,因此我们可以根据传入的参数、调用的方法、目标对象不同而返回不同的结果。
注意事项
3.1、在调用目标方法时,传入的实例对象不可以是第一个参数proxy,该参数是最终生成的代理对象,如果继续在它上面调用方法的话就又会回到该方法,从而形成死循环。
3.2、JDK动态代理只能代理java接口(interface)。
2.4、JDK动态代理在MyBatis中的运用
我们都知道Mybatis只要一个接口和一个XML Mapper(或注解),就能完成增删改查操作,其实Mybatis也是利用JDK动态代理生成的Mapper实例进行调用,下面直接看源码:
这就是生成Mapper实例的代码,其中的mapperInterface
就是我们写的Mapper接口,但有一个mapperProxy
比较重要,它就是我们说的“万能中介”,看看它是怎么实现的:
这个MapperProxy
也实现了nvocationHandler
接口,但与上面介绍的些许不同,之前的动态代理需要一个接口的实现类,但这里并没有实现类。其实JDK动态代理并不要求实现类,在invoke方法内部我们完全可以根据三个参数去“创造”结果,然后返回即可。
可以看到,Mybatis根据传入的方法“创造”了另一个“方法”MapperMethod
,然后去执行这个新“方法”,最终返回结果。这个创造方法的过程就是根据我们之前的Mapper.xml或注解获得SQL语句,然后根据传入的参数构造statement,最后就直接执行了,具体参考Mybatis源码。
2.5、JDK动态代理底层原理
先看生成的代理类
1.1、保存代理类
1
2
3
4
5
6//方法1
byte[] bytes = ProxyGenerator.generateProxyClass("stuMapperProxy", stuMapper.getClass().getInterfaces());//获取代理类bytes数组
com.google.common.io.Files.write(bytes, new File("proxy$0.class"));//保存bytes数组
//方法2:代码中加入下面一条属性,默认保存的class文件在项目根目录下com/sun/proxy下面
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");1.2、反编译代理类(以下代码精简掉无关逻辑及异常处理部分)
点击查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48public final class stuMapperProxy extends Proxy implements StuMapper {
private static Method m0; //hashCode
private static Method m1; //equals
private static Method m2; //toString
private static Method m3; //业务方法selectAll
private static Method m4; //业务方法addStudent
// 代理类继承自Proxy,Proxy中有一个InvocationHandler成员,它就是我们需要实现的那一部分。
public stuMapperProxy(InvocationHandler var1) throws {
super(var1);
}
//equals方法,通过InvocationHandler调用我们的目标对像的equals范方法
//下面的其他方法同理。
public final boolean equals(Object var1) throws {
//super.h就是InvocationHandler实例,其中的invoke方法被我们增强过
//this: 当前代理对象
//m1: 当前方法
//m2: 方法参数
return (Boolean) super.h.invoke(this, m1, new Object[]{var1});
}
public final String toString() throws {
return (String) super.h.invoke(this, m2, (Object[]) null);
}
//代理的业务方法
public final List selectAll() throws {
return (List) super.h.invoke(this, m3, (Object[]) null);
}
//代理的业务方法
public final void addStudent(int var1, String var2, int var3, String var4) throws {
super.h.invoke(this, m4, new Object[]{var1, var2, var3, var4});
}
public final int hashCode() throws {
return (Integer) super.h.invoke(this, m0, (Object[]) null);
}
//前3个方法{m0,m1,m2}固定为hashCode, equals, toString
//从后面开始就是我们定义的业务方法
//这些方法都是通过反射的方式获取到的
static {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
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.sptest.mapper.StuMapper").getMethod("selectAll");
m4 = Class.forName("com.sptest.mapper.StuMapper").getMethod("addStudent", Integer.TYPE, Class.forName("java.lang.String"), Integer.TYPE, Class.forName("java.lang.String"));
}
}这里注意:生成的代理类已经继承了
Proxy
类,无法再继承其他类,所以只能实现业务接口,这也就是为什么JDK只能代理接口的原因。1.3、代理类调用流程
代理对象生成过程(省略安全检查及异常处理代码)
2.1、从
newProxyInstance
入手点击查看代码
1
2
3
4
5
6
7
8
9public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
final Class<?>[] intfs = interfaces.clone();
//获取代理类[重要]
Class<?> cl = getProxyClass0(loader, intfs);
//获取代理类构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
//通过构造方法创建代理对象并返回
return cons.newInstance(new Object[]{h});
}2.2、生成代理类
点击查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing 如果代理类已经被给定的类加载器加载了
// the given interfaces exists, this will simply return the cached copy; 并且给定的接口也存在,则直接从缓存中拷贝一份返回即可
// otherwise, it will create the proxy class via the ProxyClassFactory 否则,就通过ProxyClassFactory创造一个代理类
return proxyClassCache.get(loader, interfaces);
}
//这个proxyClassCache是通过如下方式初始化的,proxyClassCache仅仅是一个k-v缓存,获取值时如果不存在就从给定的factory创造一个
//给定的factory有KeyFactory,用于获取生成key, ProxyClassFactory用于生成value,也就是代理类
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());2.3、来到
ProxyClassFactory
点击查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// 生成的代理类名前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 代理类名后缀生成器,其实就是一个递增数字,用AtomicLong实现
private static final AtomicLong nextUniqueNumber = new AtomicLong();
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
//需要代理的接口
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
//代理类包名
String proxyPkg = null;
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
//省略其他包名生成,策略...
if (proxyPkg == null) {
//使用默认包名 com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
//生成类名后缀(数字),最终就像$Proxy0这样
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//生成代理类字节码【核心】
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
//装载代理类,返回class对象
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
}
}2.4、继续探索
ProxyGenerator.generateProxyClass
点击查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122//先看看ProxyGenerator都有些什么
//定义了一堆常量
private static final int CLASSFILE_MAJOR_VERSION = 49;
private static final int CLASSFILE_MINOR_VERSION = 0;
private static final int CONSTANT_UTF8 = 1;
...
//定义了一堆指令
private static final int opc_iload = 21;
private static final int opc_lload = 22;
...
private static final String superclassName = "java/lang/reflect/Proxy"; //生成的代理类的父类名称
private static final String handlerFieldName = "h"; //生成的代理类的父类(Proxy)中InvocationHandler成员的名字(代理类需要使用)
//是否需要保存生成的文件,系统变量设置了 sun.misc.ProxyGenerator.saveGeneratedFiles 为 true 就保存代理类
private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
private static Method hashCodeMethod; //生成的代理类的hashCode方法
private static Method equalsMethod; //生成的代理类的equals方法
private static Method toStringMethod; //生成的代理类的toString方法
private String className; //生成的代理类全类名 如:com.sun.proxy.$Proxy0
private Class<?>[] interfaces; //生成的代理类需要实现的接口,就是我们代理的接口
private int accessFlags;//生成的代理类的访问标志(Modifiers),一般是17(1+16),1:public, 16:final
private ProxyGenerator.ConstantPool cp = new ProxyGenerator.ConstantPool();//代理类常量池,一些符号引用等
private List<ProxyGenerator.FieldInfo> fields = new ArrayList();//生成的代理类的成员变量
private List<ProxyGenerator.MethodInfo> methods = new ArrayList();//生成的代理类的方法列表(这是新生成的方法)
private Map<String, List<ProxyGenerator.ProxyMethod>> proxyMethods = new HashMap();//被代理的方法,方法签名与方法之间的映射(这是目标方法,最终用成员变量表示)
private int proxyMethodCount = 0;//代理方法数量(一般是3+业务方法数)
//核心方法:generateProxyClass,注意这个方法是public static 的,我们在其他地方也可以直接使用
public static byte[] generateProxyClass(final String className, Class<?>[] interfaces, int accessFlags) {
ProxyGenerator generator = new ProxyGenerator(className, interfaces, accessFlags);
//生成代理类class文件【核心】
final byte[] classFile = generator.generateClassFile();
if (saveGeneratedFiles) {
//保存文件
}
return classFile;
}
//核心方法:generateClassFile
private byte[] generateClassFile() {
//生成proxy代理类的hashcode,equals,toString方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//添加各个接口的方法
for (int i = 0; i < interfaces.length; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
addProxyMethod(methods[j], interfaces[i]);
}
}
//检查返回类型
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
//添加生成的构造方法
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
//将目标方法放到成员变量中
fields.add(new FieldInfo(pm.methodFieldName, "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
//新增一个与之对应的方法(签名一致),然后在该方法中调用目标方法
methods.add(pm.generateMethod());
}
}
//添加静态初始化块,就是生成代理类中的静态块
methods.add(generateStaticInitializer());
//构造常量池(cp=constant pool)
cp.getClass(dotToSlash(className));//dotToSlash点转换为斜线
cp.getClass(superclassName);
for (int i = 0; i < interfaces.length; i++) {
cp.getClass(dotToSlash(interfaces[i].getName()));
}
//常量池设置为只读模式
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
//以下是class文件的结构,想深入了解的话可以看深入Java虚拟机
// u4 magic number;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION);
// write constant pool
cp.write(dout);
// u2 access_flags;
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (int i = 0; i < interfaces.length; i++) {
dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
}
// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
return bout.toByteArray();
}