Java 动态代理

为什么需要代理

不希望或是不能直接访问某一对象 A,而是通过代理对象 B 间接访问。这种方式我们就称为代理。
这里对象 A 就称为委托类,也称为被代理类,对象 B 称为代理类。

代理有哪些优点?

  1. 隐藏委托类;
  2. 解耦。在不改变委托类的情况下做一些额外处理,比如添加初始判断及其他公共操作;
  3. AOP 编程;

静态代理

java-static-proxy-uml

代理类 ProxySubject 中的方法,都指定地调用 RealSubject 中对应的方法。

示例:

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
//定义委托行为
interface IHello {
void sayHello();

void sayHi();
}

//委托类或被代理类
class RealHello implements IHello {

@Override public void sayHello() {
System.out.println("Real hello!");
}

@Override public void sayHi() {
System.out.println("Real hai!");
}
}

//代理类,需要引用委托类
class ProxyHello implements IHello {
private IHello proxy;

private ProxyHello(IHello proxy) {
this.proxy = proxy;
}

@Override public void sayHello() {
System.out.println("Proxy, before hello!");
proxy.sayHello();
}

@Override public void sayHi() {
System.out.println("Proxy, before hi!");
proxy.sayHi();
}
}

private static void test() {
//创建被代理对象或委托类实例
IHello realHello = new RealHello();
//创建代理类
ProxyHello proxyHello = new ProxyHello(realHello);
//通过代理对象调用委托对象的方法。
proxyHello.sayHello();
proxyHello.sayHi();
}

动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// package com.example.dynamicproxy

private static void test() {
//委托类(被代理类)
RealHello helloImpl = new RealHello();
IHello proxy = (IHello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(),
HelloImpl.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println("before method invoke!");
Object returnValue = method.invoke(helloImpl, args);
System.out.println("after method invoke!");
return returnValue;
}
});
proxy.sayHi();
proxy.sayHello();
//com.example.dynamicproxy.$Proxy0
System.out.println("Proxy SimpleName: " + proxy.getClass().getSimpleName());
}

动态代理工作的基本模式是对 Proxy 对象的方法调用都会转发到 InvocationHandler#invoke() 方法中,再通过反射调用委托对象的方法。

如下图所示:

java-dynamic-proxy

动态代理有两个角色:

1. InvocationHandler 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package java.lang.reflect;

/**
* {@code InvocationHandler} is the interface implemented by
* the <i>invocation handler</i> of a proxy instance.
*
* <p>Each proxy instance has an associated invocation handler.
* When a method is invoked on a proxy instance, the method
* invocation is encoded and dispatched to the {@code invoke}
* method of its invocation handler.
*/
public interface 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.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

2. Proxy 类

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
package java.lang.reflect;

/**
* {@code Proxy} provides static methods for creating dynamic proxy
* classes and instances, and it is also the superclass of all
* dynamic proxy classes created by those methods.
*/
public class Proxy implements java.io.Serializable {
//生成的代理对象有且仅有一个包含 InvocationHandler 参数的构造函数。
/** parameter types of a proxy class constructor */
private static final Class<?>[] constructorParams = { InvocationHandler.class };

//缓存生成的代理类
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

protected InvocationHandler h;

private Proxy() {
}

protected Proxy(InvocationHandler h) {
this.h = h;
}

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException {

final Class<?>[] intfs = interfaces.clone();
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
}

public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) {
final Class<?>[] intfs = interfaces.clone();
...
return getProxyClass0(loader, intfs);
}

/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//接口数量不能超过 65535
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}

//从缓存中获取代理类,没有则通过 ProxyClassFactory#apply() 方法生成
return proxyClassCache.get(loader, interfaces);
}

/**
* A factory function that generates, defines and returns the proxy class given the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {

// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";

@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
/*
1. 校验 interfaceClass 是否被同一个 ClassLoader 加载、是否是接口、是否重复
2. 校验非公有接口是否在同一个包中
*/

// package to define proxy class in
String proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";//com.sun.proxy.

//com.sun.proxy.$Proxy0
String proxyName = proxyPkg + proxyClassNamePrefix + num;

//生成代理类字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
}
}

private static native Class<?> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
}
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
package sun.misc;

/**
* ProxyGenerator contains the code to generate a dynamic proxy class
* for the java.lang.reflect.Proxy API.
*
* The external interfaces to ProxyGenerator is the static
* "generateProxyClass" method.
*/
public class ProxyGenerator {

/**
* Generate a proxy class given a name and a list of proxy interfaces.
*
* @param name the class name of the proxy class
* @param interfaces proxy interfaces
* @param accessFlags access flags of the proxy class
*/
public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) {
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();
return classFile;
}

/**
* Generate a class file for the proxy class. This method drives the
* class file generation process.
*/
private byte[] generateClassFile() {

/* ============================================================
* Step 1: Assemble ProxyMethod objects for all methods to
* generate proxy dispatching code for.
*/

/*
* Record that proxy methods are needed for the hashCode, equals,
* and toString methods of java.lang.Object. This is done before
* the methods from the proxy interfaces so that the methods from
* java.lang.Object take precedence over duplicate methods in the
* proxy interfaces.
*/
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);

/* ============================================================
* Step 2: Assemble FieldInfo and MethodInfo structs for all of
* fields and methods in the class we are generating.
*/

/* ============================================================
* Step 3: Write the final class file.
*/

ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);

dout.writeInt(0xCAFEBABE);

return bout.toByteArray();
}
}

代理对象的创建步骤:

  1. 获取委托类的所有接口列表;
  2. proxyClassCache 查找是否缓存有该代理对象;
  3. 没有则调用 ProxyClassFactory#apply() 方法,生成代理对象;
  4. 根据接口所在包,确定生成的代理类的类名,系统默认类名为 com.sun.proxy.$Proxy0
  5. 通过 ProxyGenerator.generateProxyClass() 方法动态创建代理类的字节码文件;
  6. 通过 Proxy#defineClass0() 本地方法将对应的字节码转换为对应的 Class 对象;
  7. 以 InvocationHandler 为参数,反射生成代理对象;
  8. 当调用委托类方法的时候,都会调用 InvocationHandler#invoke() 方法,进而通过反射调用委托类的相关方法。

生成的代理类反编译后如下:

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
public final class HelloImpl extends Proxy implements IHello {
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m2;
private static Method m0;

public HelloImpl(InvocationHandler var1) throws {
super(var1);
}

public final int hashCode() throws {}

public final boolean equals(Object var1) throws {}

public final String toString() throws {}

public final void sayHello() throws {
super.h.invoke(this, m4, (Object[])null);
}

public final void sayHi() throws {
super.h.invoke(this, m3, (Object[])null);
}

static {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("com.example.dynamicproxy.IHello").getMethod("sayHello");
m3 = Class.forName("com.example.dynamicproxy.IHello").getMethod("sayHi");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
}
}

字节码框架

ASM

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为,分析类信息,甚至能够根据用户要求生成新类。

CGLIB

CGLIB(Code Generation Library) 是一个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理,无法处理 final 的情况。

参考

[1] sun.misc.ProxyGenerator.java
[2] ASM is an all purpose Java bytecode manipulation and analysis framework
[3] AOP 的利器:ASM 3.0 介绍 - IBM
[4] cglib - Byte Code Generation Library is high level API to generate and transform Java byte code