序
最近在重新回顾以往的知识点,发现之前写的java
动态代理部分并不是十分详细,有很多地方没有讲解地十分清楚,所以依靠这次机会,想着可以更加清晰地表达出这块的知识。
代理模式
要说到动态代理,我们不得不先去讲讲java
设计模式中的代理模式。代理模式,顾名思义,主要起到代理的作用,即针对客户请求由代理来负责,代理不负责具体的业务逻辑,仅仅起到一个“中间商”的作用,其最终效果是让客户端觉得自己在访问服务端。在java
设计模式中算是比较常见的一种设计模式,其基本的代码如下所示:
代码来源:javadoop.代理模式
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
|
public interface FoodService {
Food makeChicken();
Food makeNoodle();
}
public class FoodServiceImpl implements FoodService {
public Food makeChicken() {
Food f = new Chicken()
f.setChicken("1kg");
f.setSpicy("1g");
f.setSalt("3g");
return f;
}
public Food makeNoodle() {
Food f = new Noodle();
f.setNoodle("500g");
f.setSalt("5g");
return f;
}
}
// 代理要表现得“就像是”真实实现类,所以需要实现 FoodService
public class FoodServiceProxy implements FoodService {
// 内部一定要有一个真实的实现类,当然也可以通过构造方法注入
private FoodService foodService = new FoodServiceImpl();
public Food makeChicken() {
System.out.println("我们马上要开始制作鸡肉了");
// 如果我们定义这句为核心代码的话,那么,核心代码是真实实现类做的,
// 代理只是在核心代码前后做些“无足轻重”的事情
Food food = foodService.makeChicken();
System.out.println("鸡肉制作完成啦,加点胡椒粉"); // 增强
food.addCondiment("pepper");
return food;
}
public Food makeNoodle() {
System.out.println("准备制作拉面~");
Food food = foodService.makeNoodle();
System.out.println("制作完成啦")
return food;
}
}
|
从上面的代码中我们可以看到,代理类FoodServiceProxy
仅仅做了一些边边角角的事情,具体的核心业务主要还是由FoodServiceImpl
类来实现。
图片来源:javadoop.代理模式
从上图中,我们可以发现,代理模式说白了就是方法的增强与包装,客户端的调用最终还是要落实到具体的实现类中。这样的模式在Spring
中有着很多的使用,与我们不同的是,Spring
中不再需要我们手动的实现这样的代理模式,Spring
中使用的是基于jdk
动态代理和cglib
动态代理来进行包装实现的。我们明白了这两种动态代理的原理,那么Sprig
的面向切面编程也没有了很大的压力。
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
|
public interface IHello {
void say();
}
public class HelloImpl implements IHello {
@Override
public void say() {
System.out.println("hello!");
}
}
public class DynamicProxy implements InvocationHandler {
private Object object;
public Object bind(Object object) {
this.object = object;
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("ready to say : ");
return method.invoke(this.object, args);
}
}
public class Main {
public static void main(String[] args) {
IHello hello = new HelloImpl();
IHello iHello = (IHello) new DynamicProxy().bind(hello);
iHello.say();
}
}
|
可以看到在jdk
动态代理中,我们先定义了一个接口IHello
,之后通过创建一个类HelloImpl
来实现了该接口,到此为止,我们应该还是可以看懂的,因为这和之前代理模式中我们使用的方式是一样的,后面创建一个DynamicProxy
类,以及使用该类进行一系列的操作,可能比较模糊,那么我们现在着重分析分析这个类里面的两个方法bind()
和invoke()
到底做了什么。
首先来看一下bind()
方法
1
2
3
4
5
6
|
public Object bind(Object object) {
this.object = object;
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
this);
}
|
该方法中最后调用了Proxy.newProxyInstance()
方法,我们进去看看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//jdk版本 10
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);
final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();
/*
* 动态生成指定代理类和构造方法
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
|
我们接着查看getProxyConstructor()
方法
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
|
private static Constructor<?> getProxyConstructor(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces)
{
// 依据是否是单例
if (interfaces.length == 1) {
Class<?> intf = interfaces[0];
if (caller != null) {
checkProxyAccess(caller, loader, intf);
}
//java8写法 lambda表达式
//先从缓存中获取,如果缓冲中不存在,则新build一个
//我们这里肯定直接build一个
return proxyCache.sub(intf).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
} else {
// interfaces cloned
final Class<?>[] intfsArray = interfaces.clone();
if (caller != null) {
checkProxyAccess(caller, loader, intfsArray);
}
final List<Class<?>> intfs = Arrays.asList(intfsArray);
return proxyCache.sub(intfs).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
}
}
|
继续进入build()
方法中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
Constructor<?> build() {
//关键点在这里,这里定义一个代理类
Class<?> proxyClass = defineProxyClass(module, interfaces);
final Constructor<?> cons;
try {
cons = proxyClass.getConstructor(constructorParams);
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
return cons;
}
|
再进入defineProxyClass()
方法
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
|
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL; // non-public, final
String pkg = intf.getPackageName();
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// all proxy interfaces are public
proxyPkg = m.isNamed() ? PROXY_PACKAGE_PREFIX + "." + m.getName()
: PROXY_PACKAGE_PREFIX;
} else if (proxyPkg.isEmpty() && m.isNamed()) {
throw new IllegalArgumentException(
"Unnamed package cannot be added to " + m);
}
if (m.isNamed()) {
if (!m.getDescriptor().packages().contains(proxyPkg)) {
throw new InternalError(proxyPkg + " not exist in " + m.getName());
}
}
/*
* Choose a name for the proxy class to generate.
* 生成的动态代理类名字
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg.isEmpty()
? proxyClassNamePrefix + num
: proxyPkg + "." + proxyClassNamePrefix + num;
ClassLoader loader = getLoader(m);
trace(proxyName, m, loader, interfaces);
/*
* Generate the specified proxy class.
* 生成动态代理类
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
try {
Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile,
0, proxyClassFile.length,
loader, null);
reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
return pc;
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
|
从defineProxyClass()
方法中我们可以看到,在生成代理类之前,需要有很多的准备工作,包括生成的类的作用域,名称,修饰属性,类加载器等等。
好了,一切准备工作都已经完成,接下来我们就要看看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
|
static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
//真正解析生成二进制字节码的地方
final byte[] classFile = gen.generateClassFile();
//通过设置saveGeneratedFiles值,可以将字节码保存到文件
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
int i = name.lastIndexOf('.');
Path path;
if (i > 0) {
Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
Files.createDirectories(dir);
path = dir.resolve(name.substring(i+1, name.length()) + ".class");
} else {
path = Paths.get(name + ".class");
}
//将二进制字节码写入文件中
Files.write(path, classFile);
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
return classFile;
}
|
到此,我们知道了bind()
方法起到的作用是动态地生成一个代理类,返回生成的代理类。但是我们还不清楚invoke()
方法和bind()
方法的关系,所以目前我们还有以下几点疑问:
- 为什么生成的代理类可以去调用
say()
方法
- 为什么调用
say()
方法会触发invoke()
方法?
带着上面的疑问,我们继续往下看,我们知道,在生成动态类的方法里面有一个字段saveGeneratedFiles
,它是用来决定是否生成代理类文件的,那么我们想办法将它设置为true
,是不是就可以直接生成代理类的文件?
我们在之前的main()
方法中添加下面一行代码,然后执行
1
|
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
|
运行之后便可以看到该文件已经存在于项目的com.sun.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
|
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import com.example.demo.web.IHello;
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 IHello {
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 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("com.example.demo.web.IHello").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());
}
}
}
|
从反编译的代码中我们可以看到,该动态代理类实现了IHello
接口,同时继承了Proxy类,并且在其构造方法中传递了InvocationHandler
参数到该动态类中。因为实现IHello
接口的关系,该动态类中实现了say()
方法,而在方法内部,调用了invoke()方法,至此我们明白了该动态代理类的原理。
总结
首先,我们自己定义的DynamicProxy
类中的bind()
方法主要生成了一个动态的代理类,该动态代理类继承了Proxy
类,同时实现了IHello
接口,其中Proxy
类主要用来接收bind()
方法中传递过来的InvocationHandler
实例,IHello
接口的实现让动态代理类中拥有了可以被调用的方法say()
,这样在main()
方法中动态代理类通过调用say()
方法,达到调用invoke()
方法,实现代理的功能。