0%

Java - 动态代理

代理模式,是设计模式中经常用到的模式之一,它给某个对象提供一个代理对象,由这个代理对象去控制原有对象的使用。

我们可以将代理模式分为静态代理和动态代理,静态代理也就是我们常用的方式,通过编写源代码去实现代理的逻辑;而动态代理则是在运行时利用Java的反射等技术来实现的。

静态代理

我们先来看个简单的静态代理的例子:

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
public interface IHelloWord {
void say();
}

public class HelloWordImpl implements IHelloWord {
@Override
public void say() {
System.out.println("Hello word");
}
}

public class HelloWordProxy implements IHelloWord {
private IHelloWord owner;

public HelloWordProxy(IHelloWord owner) {
this.owner = owner;
}

@Override
public void say() {
System.out.println("Before");
owner.say();
System.out.println("End");
}
}

public static void main(String[] args) {
IHelloWord obj = new HelloWordProxy(new HelloWordImpl());
obj.say();
}

通过编写源代码的形式,将代理逻辑都封装在Proxy里面,然后进行调用,从而实现静态代理模式。

动态代理

如果我们有很多个类,都需要实现代理,那么使用静态代理无疑是一个重复而且乏味的编码过程,当然你也可以写个代码生成器,不过使用动态代理可以省去这些事情。

JDK动态代理

利用JDK动态代理,我们不需要手动编写源代码,先来看个例子:

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
public interface IHelloWord {
void say();
}

public class HelloWordImpl implements IHelloWord {
@Override
public void say() {
System.out.println("Hello word");
}
}

public class HelloWordHandler implements InvocationHandler {

private Object object;

public HelloWordHandler(final Object object) {
this.object = object;
}

// proxy: 被代理的类
// method:调用的方法
// args:方法参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before");
Object result = method.invoke(object, args);
System.out.println("End");
return result;
}
}

public static void main(String[] args) {
IHelloWord obj = new HelloWordImpl();
IHelloWord proxy = (IHelloWord) Proxy
.newProxyInstance(IHelloWord.class.getClassLoader(), new Class[]{IHelloWord.class},
new HelloWordHandler(obj));
proxy.say();
}

执行结果:

Before
Hello word
End

我们定义了一个HelloWordHandler类,继承自Java反射包中的InvocationHandler,用来代替静态代理中的HelloWordProxy类,然后利用Proxy,创建了实现了IHelloWord接口的某个类的一个对象,这个对象就是我们的代理对象,而这个类,是运行时动态创建的一个类。

接下来我们跟踪一下源码,看看这个动态类是怎么生成的,先看看Proxy类的newProxyInstance函数:

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

private static final Class<?>[] constructorParams = { InvocationHandler.class };

// loader: 传入的IHelloWord的类加载器
// interfaces:代理对象需要实现的接口
// h:一个InvocationHandler对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}

// 生成代理类的字节码,如果已经生成了,则会被缓存起来
// 注意:代理类会继承自Proxy,并且有一个带有InvocationHandler类型参数的构造函数,在下面会讲解
Class<?> cl = getProxyClass0(loader, intfs);

try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}

// 找到生成的代理类中,含有InvocationHandler类型参数的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 调用构造函数,生成代理类的对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}

newProxyInstance函数相当于是干了两件事情:

  • 动态生成代理类的字节码;
  • 调用构造函数,构造一个代理类的对象。

继续看看动态类是怎么生成的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Proxy implements java.io.Serializable {

private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// ...
}
}

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}

// WeakCache#get方法细节就不太深入了
// get方法会判断是否已经有缓存了,如果没有的话,最终会调用到ProxyClassFactory#apply方法中,用于生成代理类的字节码
return proxyClassCache.get(loader, interfaces);
}
}

所以核心的动态生成代理类的字节码的逻辑是在Proxy的内部类ProxyClassFactory的apply方法中:

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
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// 动态类的类名前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 一个计数器,统计生成的代理类数量,同时这个数量也是动态生成的类名的一部分
private static final AtomicLong nextUniqueNumber = new AtomicLong();

@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// 校验过程
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
// 验证类加载器是否能通过接口名称加载类
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}

// 校验该类是否是一个interface
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}

// 校验接口重复
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}

String proxyPkg = null;
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

// 验证所有非public的接口的包名是否相同
// 代理类的报名会跟非public的接口的包名保持一致
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}

if (proxyPkg == null) {
// 默认使用com.sun.proxy包名
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}

// 构造代理类的名称
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;

// 生成字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}

真正生成字节码的方法是ProxyGenerator.generateProxyClass,我们可以添加如下代码,将生成的字节码保存在本地文件:

1
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

保存之后,上例中,会生成com.sun.proxy.$Proxy0类,class文件内容为:

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

package com.sun.proxy;

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 IHelloWord {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;

public $Proxy0(InvocationHandler var1) throws {
// Proxy的构造函数接收一个InvocationHandler类型参数,并保存到成员变量h中
// h的定义为:protected InvocationHandler h;
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 void say() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

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("IHelloWord").getMethod("say");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}

$Proxy0这个类,即是动态生成的代理类,也即是main函数中,Proxy.newProxyInstance返回的IHelloWord对象,继承自Proxy,并且实现了IHelloWord接口,从.class文件中可以看出,调用这个类的方法,最终都会调用到InvocationHandler接口的invoke方法中,本例中,即是调用到了HelloWordHandler的invoke方法中。

CGLIB动态代理

由于JDK的动态代理是通过实现接口的方式来实现的动态代理,因此需要被代理的类必须通过接口实现业务逻辑,才能被动态代理,如果没有实现任何接口,则无法使用JDK动态代理。而另外一种CGLIB代理,则没有这个限制,先来个例子:

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
public class HelloWordService {
public void say() {
System.out.println("Hello word");
}
}

public class HelloWordProxy implements MethodInterceptor {
// o:动态生成的代理对象
// method:被代理的方法
// objects:方法参数
// methodProxy:代理的方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before");
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("End");
return object;
}
}

public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloWordService.class);
enhancer.setCallback(new HelloWordProxy());
HelloWordService proxy = (HelloWordService) enhancer.create();
proxy.say();
}

执行结果:

Before
Hello word
End

可以看到,使用CGLIB的方式,被代理的类不需要实现什么接口就可以使用,而main方法中的代码也比较简单,将HelloWordService设置到Enhancer的superclass成员变量中,将HelloWordProxy对象设置到Enhancer的callbacks成员变量中:

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
public class Enhancer extends AbstractClassGenerator {
private Class superclass;
private Callback[] callbacks;

public void setSuperclass(Class superclass) {
if (superclass != null && superclass.isInterface()) {
setInterfaces(new Class[]{ superclass });
} else if (superclass != null && superclass.equals(Object.class)) {
this.superclass = null;
} else {
this.superclass = superclass;
}
}

public void setCallback(final Callback callback) {
setCallbacks(new Callback[]{ callback });
}

public void setCallbacks(Callback[] callbacks) {
if (callbacks != null && callbacks.length == 0) {
throw new IllegalArgumentException("Array cannot be empty");
}
this.callbacks = callbacks;
}
}

然后利用Enhancer的create()方法构建了一个新对象,这个对象即是我们的代理对象。但是这个对象并不是HelloWordService类型,而是HelloWordService的子类。

同样,我们也可以在main函数中添加如下代码,将动态生成的类保存到本地:

1
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");

上例中,会生成三个.class文件:

HelloWordService$$EnhancerByCGLIB$$4fc62686$$FastClassByCGLIB$$e944bfc7.class

HelloWordService$$EnhancerByCGLIB$$4fc62686.class

HelloWordService$$FastClassByCGLIB$$1cccc666.class

其中,HelloWordService$$EnhancerByCGLIB$$4fc62686.class,便是最终的代理对象的类型:

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
public class HelloWordService$$EnhancerByCGLIB$$4fc62686 extends HelloWordService implements Factory {

private MethodInterceptor CGLIB$CALLBACK_0;
// 被代理的方法
private static final Method CGLIB$say$0$Method;
// 代理方法
private static final MethodProxy CGLIB$say$0$Proxy;

static void CGLIB$STATICHOOK1() {
// 代理类
Class var0 = Class.forName("proxy.cglib.HelloWordService$$EnhancerByCGLIB$$4fc62686");
// 被代理类
Class var1;

// 代理方法
CGLIB$say$0$Method = ReflectUtils.findMethods(new String[]{"say", "()V"}, (var1 = Class.forName("proxy.cglib.HelloWordService")).getDeclaredMethods())[0];
// 被代理方法
CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$0");
}

final void CGLIB$say$0() {
super.say();
}

public final void say() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}

if (var10000 != null) {
// 调用拦截器的方法,此例中,即是HelloWordProxy
var10000.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
} else {
super.say();
}
}
}

在HelloWordService的子类中,每个方法都有两个相关联的成员变量,一个是Method CGLIB$***$0$Method,一个是MethodProxy CGLIB$***$0$Proxy,当调用子类的方法时,会调用到MethodInterceptor的intercept方法中,最终实现了代理业务逻辑。

PS:关于MethodProxy,以及CGLIB的FastClass机制,可以参考文末的文章。

CGLIB是通过动态生成一个继承自目标类的子类的方式,来实现的动态代理,因此被代理类中,被final修饰的方法由于无法被子类重写,因此无法进行动态代理。

参考



-=全文完=-