代理模式及实现探究

前言

有使用过Spring并且有过Debug经历的程序员或者熟悉Spring实现原理的开发应该知道动态代理Spring框架实现的一大重要技术工具。

先看下图:

使用Spring依赖注入的bean通常都能看到CGLIB的标识。而我们自定义的类对象查看到对象信息基本和类定义无差。

究其原由是因为Spring依赖注入的bean并非原始的类对象,而是使用CGLIB的代理对象。

借由此本文旨在对代理模式及实现一探究竟。


代理模式介绍

概述

因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ?

你有想过限制访问某个对象,也就是说,提供一组方法给普通用户,特别方法给管理员用户?

以上两种需求都非常类似,并且都需要解决一个更大的问题:
你如何提供一致的接口给某个对象让它可以改变其内部功能,或者是从来不存在的功能?

可以通过引入一个新的对象,来实现对真实对象的操作或者将新的对象作为真实对象的一个替身。即代理对象。它可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。

用书面术语来总结:

代理模式是常用的java设计模式。

它的特征是代理类与委托类有同样的接口。

代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 

分类

按照使用场景:

  • 远程代理(Remote Proxy): 为一个位于不同的地址空间的对象提供一个本地的代理对象。这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又叫做大使(Ambassador)
  • 虚拟代理(Virtual Proxy): 根据需要创建开销很大的对象。如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
  • 保护代理(Protection Proxy): 控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
  • 智能指引(Smart Reference): 取代了简单的指针,它在访问对象时执行一些附加操作。
  • Copy-on-Write代理: 它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。

按照实现方式:

  • 静态代理: 在程序运行前就已经存在代理类的字节码文件。代理类和委托类的关系在运行前就确定了
  • 动态代理: 动态代理类的源码是在程序运行期由JVM根据反射机制动态生成的。代理类和委托类的关系是在程序运行时确定的

UML图示


跟着Demo深入探究

接下来我们自己动手用demo来实践一下代理模式的实现,并比较不同实现方式的区别。

公共接口及类定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package common;
/**
* 原始接口
*/
public interface TestService {
/**
* 打印入参字符串
*/
public void saySomething(String str);
/**
* 返回入参自增+1
*/
public int countInt(int num);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package common;
public class TestServiceImpl implements TestService {
@Override
public void saySomething(String str) {
System.out.println(str);
}
public int countInt(int num) {
return (num++);
}
}

静态代理实现demo

我们先看静态代理如何完成上述接口实现的代理:

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
package quiescent.demo.proxy;
import java.util.Date;
import common.TestService;
/**
* 静态代理类
*/
public class ProxySubject implements TestService {
// 代理类持有一个委托类的对象引用
private TestService testService;
public ProxySubject(TestService testService) {
this.testService = testService;
}
/**
* 将请求分派给委托类执行,记录任务执行前后的时间,时间差即为任务的处理时间
*
* @param taskName
*/
@Override
public void saySomething(String something) {
Date startDate = new Date();
System.out.println("开始调用目标类时时间点:" + startDate);
// 将请求分派给委托类处理
testService.saySomething(something);
Date endDate = new Date();
System.out.println("结束调用目标类时时间点:" + endDate);
}
@Override
public int countInt(int num) {
return testService.countInt(num);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 测试客户端
*/
package quiescent.demo.proxy;
import common.TestService;
import common.TestServiceImpl;
public class Client {
public static void main(String[] args) {
TestService proxy = new ProxySubject(new TestServiceImpl());
proxy.saySomething("Hello buddy");
}
}

下面是上述代理执行结果:

开始调用目标类时时间点:Mon Oct 17 11:01:04 CST 2016
Hello buddy
结束调用目标类时时间点:Mon Oct 17 11:01:04 CST 2016

静态代理实现总结:

上述例子,静态代理类做的事情即是

  • 在真实调用目标接口实现时打印接口调用的请求时间
  • 调用目标接口
  • 目标接口调用结束后打印请求完成时间

我们发现:

  • 使用了静态代理。我们不需要入侵真实的目标类即可在目标对象调用时封装一套额外的逻辑。当然这是代理模式的有点,不局限于静态代理
  • 为了实现接口的代理,我们必须要定义一个代理类实现同一个接口,在实现中显示的调用目标接口来完成代理的实现
    • 基于这点,这也反应了静态代理实现的一大缺点:
      • 一个代理对象服务于同一类的对象。业务类的所有方法都需要进行代理;业务类每新增一个接口,所有的代理类也要实现这个接口,增加代码维护的复杂度

JDK动态代理Demo

上述静态代理的缺陷,而动态代理的特性正好可以解决。

而动态代理也有很多种实现技术手段。这一节讲讲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
package jdk.reflect.demo.dynamicProxy.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Calendar;
/**
* JDK动态代理类
*/
public class ProxyHandler {
public static Object getPoxyObject(final Object c) {
return Proxy.newProxyInstance(c.getClass().getClassLoader(), c.getClass().getInterfaces(), // JDK实现动态代理,但JDK实现必须需要接口
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object reObj = null;
reObj = method.invoke(c, args);
if (method.getName().equals("saySomething")) {
System.out.println("at [" + Calendar.getInstance().get(Calendar.HOUR) + ":"
+ Calendar.getInstance().get(Calendar.MINUTE) + ":"
+ Calendar.getInstance().get(Calendar.SECOND) + "]\n");
}
return reObj;
}
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 测试客户端
*/
package jdk.reflect.demo.dynamicProxy.service;
import common.TestService;
import common.TestServiceImpl;
public class JDKServiceMain {
public static void main(String[] args) {
TestService service = new TestServiceImpl();
TestService poxyService = (TestService) ProxyHandler.getPoxyObject(service);
System.out.println("\n\nexcute info:\n");
poxyService.saySomething("Manager Zhou: Hello, GentleSong.");
poxyService.saySomething("Manager Zhou: you are KXF's dream guy.");
poxyService.saySomething("Manager Zhou: Are you willing to sacrifice for the happniess of KXF's buddy?");
poxyService.saySomething("GentleSong: Yes, I am.");
}
}

下面是上述代理执行结果:

excute info:

Manager Zhou: Hello, GentleSong.
at [11:35:10]

Manager Zhou: you are KXF's dream guy.
at [11:35:10]

Manager Zhou: Are you willing to sacrifice for the happniess of KXF's buddy?
at [11:35:10]

GentleSong: Yes, I am.
at [11:35:10]

JDK动态代理实现总结:

上述例子,动态代理类做的事情即是

  • 调用目标接口
  • 调用结束时打印请求调用完成时间

我们发现:

  • JDK的实现方式看,它可以实现一类接口的的代理。
    • 我们不需要每新增一个接口即新增一个代理类实现
    • 我们不需要接口定义新增或删减时同时要修改代理类
  • 但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理。

CGLIB动态代理Demo

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
28
29
30
31
32
33
34
35
36
package cglib.reflect.demo.dynamicProxy.service;
import java.lang.reflect.Method;
import java.util.Calendar;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* CGLIB动态代理类
*/
public class ProxyHandler {
public static Object getPoxyObject(Object c) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(c.getClass());
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {
proxy.invokeSuper(arg0, arg2);
if (arg1.getName().equals("saySomething")) {
System.out.println("at [" + Calendar.getInstance().get(Calendar.HOUR) + ":"
+ Calendar.getInstance().get(Calendar.MINUTE) + ":"
+ Calendar.getInstance().get(Calendar.SECOND) + "]\n");
}
return null;
}
});
return enhancer.create();
}
}
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
package cglib.reflect.demo.dynamicProxy.service;
import java.lang.reflect.Method;
import java.util.Calendar;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyHandler {
public static Object getPoxyObject(Object c) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(c.getClass());
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {
proxy.invokeSuper(arg0, arg2);
if (arg1.getName().equals("saySomething")) {
System.out.println("at [" + Calendar.getInstance().get(Calendar.HOUR) + ":"
+ Calendar.getInstance().get(Calendar.MINUTE) + ":"
+ Calendar.getInstance().get(Calendar.SECOND) + "]\n");
}
return null;
}
});
return enhancer.create();
}
}

CGLIB动态代理实现总结:

上述例子,动态代理类做的事情即是

  • 调用目标接口
  • 调用结束时打印请求调用完成时间

CGLIB实现原理:

  • CGLIB是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强(当然这运用到了java的又一特性:修改字节码)。

几种代理实现方式的比较

上述几种代理实现方式实践之后,大家是否就粗暴的认为CGLIB动态代理的方式是最优项嘞?毕竟它解决了静态代理和JDK动态代理的缺陷。

这里我们不要这么快给出判断。同样实践得真知,下面我们用demo来测试一下几种代理实现方式的性能:

测试分为以下几个维度:

  • 在单例模式(仅创建一次代理类)下分别执行100万、500万次静态代理、JDK动态代理、CGLIB动态代理
  • 在多例模式(每次调用新创建代理类)下分别执行100万、500万次静态代理、JDK动态代理、CGLIB动态代理
  • 细化测试代理类创建、代理类执行的时耗测试

PS:在单例测试和多例测试请勿按比例比较,毕竟多例测试中不断地创建及销魂对象也是时间花销。但是不同实现方式的多例测试还是有参考性的。

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
package dynamicProxy.performance.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import common.TestService;
import common.TestServiceImpl;
import jdk.reflect.demo.dynamicProxy.service.ProxyHandler;
import quiescent.demo.proxy.ProxySubject;
public class PerformanceTestMain {
public static void main(String[] args) {
System.out.println(String.format("==================== run test : [java.version=%s] ====================",
System.getProperty("java.version")));
// 预热,防止首次加载造成的测试影响
predo();
System.out.println("单例测试:\n");
SingelTestUtil.singleTest(1000000, 3);
System.out.println("\n");
SingelTestUtil.singleTest(5000000, 3);
System.out.println("\n\n\n");
System.out.println("多例测试:\n");
DuplTestUtil.duplTest(1000000, 3);
System.out.println("\n");
DuplTestUtil.duplTest(5000000, 3);
System.out.println("\n\n\n");
System.out.println("深入寻找差异原因测试:");
FindReasonTest.findReason(1000000, 3);
System.out.println("\n\n\n");
FindReasonTest.findReason(5000000, 3);
}
/**
* 预热,防止首次加载造成的测试影响
*/
private static void predo() {
for (int i = 0; i < 10000; i++) {
TestService orgnial = new TestServiceImpl();
TestService staticProxy = new ProxySubject(orgnial);
TestService jdkProxy = (TestService) ProxyHandler.getPoxyObject(orgnial);
TestService cglibProxy = (TestService) cglib.reflect.demo.dynamicProxy.service.ProxyHandler
.getPoxyObject(orgnial);
int num = 1;
num = orgnial.countInt(num);
num = staticProxy.countInt(num);
num = jdkProxy.countInt(num);
num = cglibProxy.countInt(num);
}
}
/**
* 单例测试工具类
*/
private static class SingelTestUtil {
public static void singleTest(int runCount, int repeatCount) {
System.out.println("test runCount :" + runCount + "\n");
Map<String, Long> resultMap = new HashMap<String, Long>();
resultMap.put("orginal", 0L);
resultMap.put("static proxy", 0L);
resultMap.put("jdk proxy", 0L);
resultMap.put("cglib proxy", 0L);
for (int i = 0; i < repeatCount; i++) {
System.out.println("total repeat count is :" + repeatCount + " current is : " + (i + 1));
resultMap.put("orginal", resultMap.get("orginal") + singleItemTest(runCount, "orginal").get("orginal"));
resultMap.put("static proxy",
resultMap.get("static proxy") + singleItemTest(runCount, "static proxy").get("static proxy"));
resultMap.put("jdk proxy",
resultMap.get("jdk proxy") + singleItemTest(runCount, "jdk proxy").get("jdk proxy"));
resultMap.put("cglib proxy",
resultMap.get("cglib proxy") + singleItemTest(runCount, "cglib proxy").get("cglib proxy"));
System.out.println("\n");
}
for (String key : resultMap.keySet()) {
System.out.println("execute :" + key + " " + repeatCount + " \'t, average timeCost is : "
+ resultMap.get(key) / repeatCount);
}
}
private static Map<String, Long> singleItemTest(int runCount, String key) {
Map<String, Long> resultMap = new HashMap<String, Long>();
int num = 1;
long startTime = System.currentTimeMillis();
TestService nativeTest = new TestServiceImpl();
TestService exeSetvice = null;
if ("cglib proxy".equals(key)) {
exeSetvice = (TestService) cglib.reflect.demo.dynamicProxy.service.ProxyHandler
.getPoxyObject(nativeTest);
} else if ("jdk proxy".equals(key)) {
exeSetvice = (TestService) ProxyHandler.getPoxyObject(nativeTest);
} else if ("static proxy".equals(key)) {
exeSetvice = new ProxySubject(nativeTest);
} else if ("orginal".equals(key)) {
exeSetvice = nativeTest;
}
for (int i = 0; i < runCount; i++) {
num = exeSetvice.countInt(num);
}
long endTime = System.currentTimeMillis();
resultMap.put(key, (endTime - startTime));
System.out.println(" execute " + key + " : " + (endTime - startTime) + " ms");
return resultMap;
}
}
/**
* 多例测试工具类
*/
private static class DuplTestUtil {
public static void duplTest(int runCount, int repeatCount) {
System.out.println("test runCount :" + runCount + "\n");
Map<String, Long> resultMap = new HashMap<String, Long>();
resultMap.put("orginal", 0L);
resultMap.put("static proxy", 0L);
resultMap.put("jdk proxy", 0L);
resultMap.put("cglib proxy", 0L);
for (int i = 0; i < repeatCount; i++) {
System.out.println("total repeat count is :" + repeatCount + " current is : " + (i + 1));
resultMap.put("orginal", resultMap.get("orginal") + duplItemTest(runCount, "orginal").get("orginal"));
resultMap.put("static proxy",
resultMap.get("static proxy") + duplItemTest(runCount, "static proxy").get("static proxy"));
resultMap.put("jdk proxy",
resultMap.get("jdk proxy") + duplItemTest(runCount, "jdk proxy").get("jdk proxy"));
resultMap.put("cglib proxy",
resultMap.get("cglib proxy") + duplItemTest(runCount, "cglib proxy").get("cglib proxy"));
System.out.println("\n");
}
for (String key : resultMap.keySet()) {
System.out.println("execute :" + key + " " + repeatCount + " \'t, average timeCost is : "
+ resultMap.get(key) / repeatCount);
}
}
private static Map<String, Long> duplItemTest(int runCount, String key) {
int num = 1;
Map<String, Long> resultMap = new HashMap<String, Long>();
long startTime = System.currentTimeMillis();
for (int i = 0; i < runCount; i++) {
TestService exeSetvice = null;
if ("cglib proxy".equals(key)) {
TestService nativeTest = new TestServiceImpl();
exeSetvice = (TestService) cglib.reflect.demo.dynamicProxy.service.ProxyHandler
.getPoxyObject(nativeTest);
} else if ("jdk proxy".equals(key)) {
TestService nativeTest = new TestServiceImpl();
exeSetvice = (TestService) ProxyHandler.getPoxyObject(nativeTest);
} else if ("static proxy".equals(key)) {
TestService nativeTest = new TestServiceImpl();
exeSetvice = new ProxySubject(nativeTest);
} else if ("orginal".equals(key)) {
TestService nativeTest = new TestServiceImpl();
exeSetvice = nativeTest;
}
num = exeSetvice.countInt(num);
}
long endTime = System.currentTimeMillis();
System.out.println(" execute " + key + " : " + (endTime - startTime) + " ms");
resultMap.put(key, endTime - startTime);
return resultMap;
}
}
/**
* 原因深入探索测试工具类
*/
private static class FindReasonTest {
public static void findReason(int runCount, int repeatCount) {
System.out.println("deep test. run Count is :" + runCount);
final Map<String, Long> resultMap = new HashMap<String, Long>();
resultMap.put("orginal create", 0L);
resultMap.put("orginal execute", 0L);
resultMap.put("static proxy create", 0L);
resultMap.put("static proxy execute", 0L);
resultMap.put("jdk proxy create", 0L);
resultMap.put("jdk proxy execute", 0L);
resultMap.put("cglib proxy create", 0L);
resultMap.put("cglib proxy execute", 0L);
for (int i = 0; i < repeatCount; i++) {
System.out.println("total repeat count is :" + repeatCount + " current is : " + (i + 1));
Map<String, Long> tempMap = new HashMap<String, Long>();
tempMap = findReasonItemTest(runCount, "orginal");
resultMap.put("orginal create", resultMap.get("orginal create") + tempMap.get("orginal create"));
resultMap.put("orginal execute", resultMap.get("orginal execute") + tempMap.get("orginal execute"));
tempMap = findReasonItemTest(runCount, "static proxy");
resultMap.put("static proxy create",
resultMap.get("static proxy create") + tempMap.get("static proxy create"));
resultMap.put("static proxy execute",
resultMap.get("static proxy execute") + tempMap.get("static proxy execute"));
tempMap = findReasonItemTest(runCount, "jdk proxy");
resultMap.put("jdk proxy create", resultMap.get("jdk proxy create") + tempMap.get("jdk proxy create"));
resultMap.put("jdk proxy execute",
resultMap.get("jdk proxy execute") + tempMap.get("jdk proxy execute"));
tempMap = findReasonItemTest(runCount, "cglib proxy");
resultMap.put("cglib proxy create",
resultMap.get("cglib proxy create") + tempMap.get("cglib proxy create"));
resultMap.put("cglib proxy execute",
resultMap.get("cglib proxy execute") + tempMap.get("cglib proxy execute"));
System.out.println("\n");
}
List<String> list = new ArrayList<String>() {
private static final long serialVersionUID = -574662443316876402L;
{
List<Object> objects = Arrays.asList(resultMap.keySet().toArray());
for (Object object : objects) {
add((String) object);
}
}
};
Collections.sort(list);
for (int i = 0; i < list.size(); i++) {
System.out.println("do " + list.get(i) + " " + repeatCount + " \'t, average timeCost is : "
+ resultMap.get(list.get(i)) / repeatCount);
if (i % 2 == 1) {
System.out.println("");
}
}
}
private static Map<String, Long> findReasonItemTest(int runCount, String key) {
TestService exeSetvice = null;
Map<String, Long> resultMap = new HashMap<String, Long>();
long startTime = System.currentTimeMillis();
for (int i = 0; i < runCount; i++) {
if ("cglib proxy".equals(key)) {
TestService nativeTest = new TestServiceImpl();
exeSetvice = (TestService) cglib.reflect.demo.dynamicProxy.service.ProxyHandler
.getPoxyObject(nativeTest);
} else if ("jdk proxy".equals(key)) {
TestService nativeTest = new TestServiceImpl();
exeSetvice = (TestService) ProxyHandler.getPoxyObject(nativeTest);
} else if ("static proxy".equals(key)) {
TestService nativeTest = new TestServiceImpl();
exeSetvice = new ProxySubject(nativeTest);
} else if ("orginal".equals(key)) {
TestService nativeTest = new TestServiceImpl();
exeSetvice = nativeTest;
}
}
long endTime = System.currentTimeMillis();
resultMap.put(key + " create", (endTime - startTime));
System.out.println(" Create " + key + ": " + (endTime - startTime) + " ms");
int num = 1;
startTime = System.currentTimeMillis();
for (int i = 0; i < runCount; i++) {
exeSetvice.countInt(num);
}
endTime = System.currentTimeMillis();
resultMap.put(key + " execute", (endTime - startTime));
System.out.println(" execute " + key + ": " + (endTime - startTime) + " ms");
System.out.println();
return resultMap;
}
}
}

JDK1.7下的测试结果如下:

==================== run test : [java.version=1.7.0_79] ====================
单例测试:

test runCount :1000000

total repeat count is :3 current is : 1
 execute orginal : 32 ms
 execute static proxy : 60 ms
 execute jdk proxy : 48 ms
 execute cglib proxy : 23 ms


total repeat count is :3 current is : 2
 execute orginal : 8 ms
 execute static proxy : 7 ms
 execute jdk proxy : 22 ms
 execute cglib proxy : 33 ms


total repeat count is :3 current is : 3
 execute orginal : 4 ms
 execute static proxy : 4 ms
 execute jdk proxy : 20 ms
 execute cglib proxy : 18 ms


execute :cglib proxy 3 't, average timeCost is : 24
execute :orginal 3 't, average timeCost is : 14
execute :static proxy 3 't, average timeCost is : 23
execute :jdk proxy 3 't, average timeCost is : 30


test runCount :5000000

total repeat count is :3 current is : 1
 execute orginal : 21 ms
 execute static proxy : 25 ms
 execute jdk proxy : 129 ms
 execute cglib proxy : 123 ms


total repeat count is :3 current is : 2
 execute orginal : 20 ms
 execute static proxy : 21 ms
 execute jdk proxy : 141 ms
 execute cglib proxy : 92 ms


total repeat count is :3 current is : 3
 execute orginal : 22 ms
 execute static proxy : 20 ms
 execute jdk proxy : 145 ms
 execute cglib proxy : 123 ms


execute :cglib proxy 3 't, average timeCost is : 112
execute :orginal 3 't, average timeCost is : 21
execute :static proxy 3 't, average timeCost is : 22
execute :jdk proxy 3 't, average timeCost is : 138




多例测试:

test runCount :1000000

total repeat count is :3 current is : 1
 execute orginal : 25 ms
 execute static proxy : 40 ms
 execute jdk proxy : 658 ms
 execute cglib proxy : 3197 ms


total repeat count is :3 current is : 2
 execute orginal : 15 ms
 execute static proxy : 19 ms
 execute jdk proxy : 1315 ms
 execute cglib proxy : 3852 ms


total repeat count is :3 current is : 3
 execute orginal : 15 ms
 execute static proxy : 17 ms
 execute jdk proxy : 799 ms
 execute cglib proxy : 2935 ms


execute :cglib proxy 3 't, average timeCost is : 3328
execute :orginal 3 't, average timeCost is : 18
execute :static proxy 3 't, average timeCost is : 25
execute :jdk proxy 3 't, average timeCost is : 924


test runCount :5000000

total repeat count is :3 current is : 1
 execute orginal : 37 ms
 execute static proxy : 204 ms
 execute jdk proxy : 3348 ms
 execute cglib proxy : 14302 ms


total repeat count is :3 current is : 2
 execute orginal : 39 ms
 execute static proxy : 49 ms
 execute jdk proxy : 3154 ms
 execute cglib proxy : 14122 ms


total repeat count is :3 current is : 3
 execute orginal : 39 ms
 execute static proxy : 211 ms
 execute jdk proxy : 3255 ms
 execute cglib proxy : 13464 ms


execute :cglib proxy 3 't, average timeCost is : 13962
execute :orginal 3 't, average timeCost is : 38
execute :static proxy 3 't, average timeCost is : 154
execute :jdk proxy 3 't, average timeCost is : 3252




深入寻找差异原因测试:
deep test. run Count is :1000000
total repeat count is :3 current is : 1
 Create orginal: 15 ms
 execute orginal: 4 ms

 Create static proxy: 36 ms
 execute static proxy: 9 ms

 Create jdk proxy: 588 ms
 execute jdk proxy: 41 ms

 Create cglib proxy: 2830 ms
 execute cglib proxy: 210 ms



total repeat count is :3 current is : 2
 Create orginal: 5 ms
 execute orginal: 3 ms

 Create static proxy: 6 ms
 execute static proxy: 4 ms

 Create jdk proxy: 640 ms
 execute jdk proxy: 20 ms

 Create cglib proxy: 2759 ms
 execute cglib proxy: 14 ms



total repeat count is :3 current is : 3
 Create orginal: 3 ms
 execute orginal: 3 ms

 Create static proxy: 6 ms
 execute static proxy: 4 ms

 Create jdk proxy: 689 ms
 execute jdk proxy: 19 ms

 Create cglib proxy: 2684 ms
 execute cglib proxy: 14 ms



do cglib proxy create 3 't, average timeCost is : 2757
do cglib proxy execute 3 't, average timeCost is : 79

do jdk proxy create 3 't, average timeCost is : 639
do jdk proxy execute 3 't, average timeCost is : 26

do orginal create 3 't, average timeCost is : 7
do orginal execute 3 't, average timeCost is : 3

do static proxy create 3 't, average timeCost is : 16
do static proxy execute 3 't, average timeCost is : 5





deep test. run Count is :5000000
total repeat count is :3 current is : 1
 Create orginal: 17 ms
 execute orginal: 16 ms

 Create static proxy: 37 ms
 execute static proxy: 21 ms

 Create jdk proxy: 2942 ms
 execute jdk proxy: 104 ms

 Create cglib proxy: 13697 ms
 execute cglib proxy: 74 ms



total repeat count is :3 current is : 2
 Create orginal: 96 ms
 execute orginal: 22 ms

 Create static proxy: 45 ms
 execute static proxy: 20 ms

 Create jdk proxy: 2927 ms
 execute jdk proxy: 117 ms

 Create cglib proxy: 14023 ms
 execute cglib proxy: 79 ms



total repeat count is :3 current is : 3
 Create orginal: 18 ms
 execute orginal: 21 ms

 Create static proxy: 29 ms
 execute static proxy: 18 ms

 Create jdk proxy: 2898 ms
 execute jdk proxy: 107 ms

 Create cglib proxy: 15810 ms
 execute cglib proxy: 210 ms



do cglib proxy create 3 't, average timeCost is : 14510
do cglib proxy execute 3 't, average timeCost is : 121

do jdk proxy create 3 't, average timeCost is : 2922
do jdk proxy execute 3 't, average timeCost is : 109

do orginal create 3 't, average timeCost is : 43
do orginal execute 3 't, average timeCost is : 19

do static proxy create 3 't, average timeCost is : 37
do static proxy execute 3 't, average timeCost is : 19

JDK1.6下的测试结果如下:

==================== run test : [java.version=1.6.0_65] ====================
单例测试:

test runCount :1000000

total repeat count is :3 current is : 1
 execute orginal : 44 ms
 execute static proxy : 110 ms
 execute jdk proxy : 35 ms
 execute cglib proxy : 25 ms


total repeat count is :3 current is : 2
 execute orginal : 2 ms
 execute static proxy : 4 ms
 execute jdk proxy : 22 ms
 execute cglib proxy : 21 ms


total repeat count is :3 current is : 3
 execute orginal : 4 ms
 execute static proxy : 5 ms
 execute jdk proxy : 22 ms
 execute cglib proxy : 20 ms


execute :cglib proxy 3 't, average timeCost is : 22
execute :orginal 3 't, average timeCost is : 16
execute :static proxy 3 't, average timeCost is : 39
execute :jdk proxy 3 't, average timeCost is : 26


test runCount :5000000

total repeat count is :3 current is : 1
 execute orginal : 19 ms
 execute static proxy : 21 ms
 execute jdk proxy : 110 ms
 execute cglib proxy : 111 ms


total repeat count is :3 current is : 2
 execute orginal : 17 ms
 execute static proxy : 20 ms
 execute jdk proxy : 108 ms
 execute cglib proxy : 112 ms


total repeat count is :3 current is : 3
 execute orginal : 19 ms
 execute static proxy : 20 ms
 execute jdk proxy : 106 ms
 execute cglib proxy : 109 ms


execute :cglib proxy 3 't, average timeCost is : 110
execute :orginal 3 't, average timeCost is : 18
execute :static proxy 3 't, average timeCost is : 20
execute :jdk proxy 3 't, average timeCost is : 108




多例测试:

test runCount :1000000

total repeat count is :3 current is : 1
 execute orginal : 20 ms
 execute static proxy : 31 ms
 execute jdk proxy : 1333 ms
 execute cglib proxy : 2790 ms


total repeat count is :3 current is : 2
 execute orginal : 11 ms
 execute static proxy : 13 ms
 execute jdk proxy : 1257 ms
 execute cglib proxy : 3907 ms


total repeat count is :3 current is : 3
 execute orginal : 19 ms
 execute static proxy : 18 ms
 execute jdk proxy : 2135 ms
 execute cglib proxy : 3424 ms


execute :cglib proxy 3 't, average timeCost is : 3373
execute :orginal 3 't, average timeCost is : 16
execute :static proxy 3 't, average timeCost is : 20
execute :jdk proxy 3 't, average timeCost is : 1575


test runCount :5000000

total repeat count is :3 current is : 1
 execute orginal : 77 ms
 execute static proxy : 97 ms
 execute jdk proxy : 7851 ms
 execute cglib proxy : 15401 ms


total repeat count is :3 current is : 2
 execute orginal : 49 ms
 execute static proxy : 60 ms
 execute jdk proxy : 8527 ms
 execute cglib proxy : 14931 ms


total repeat count is :3 current is : 3
 execute orginal : 92 ms
 execute static proxy : 106 ms
 execute jdk proxy : 7951 ms
 execute cglib proxy : 14286 ms


execute :cglib proxy 3 't, average timeCost is : 14872
execute :orginal 3 't, average timeCost is : 72
execute :static proxy 3 't, average timeCost is : 87
execute :jdk proxy 3 't, average timeCost is : 8109




深入寻找差异原因测试:
deep test. run Count is :1000000
total repeat count is :3 current is : 1
 Create orginal: 16 ms
 execute orginal: 5 ms

 Create static proxy: 40 ms
 execute static proxy: 6 ms

 Create jdk proxy: 1270 ms
 execute jdk proxy: 56 ms

 Create cglib proxy: 2679 ms
 execute cglib proxy: 23 ms



total repeat count is :3 current is : 2
 Create orginal: 7 ms
 execute orginal: 3 ms

 Create static proxy: 10 ms
 execute static proxy: 4 ms

 Create jdk proxy: 1320 ms
 execute jdk proxy: 27 ms

 Create cglib proxy: 2956 ms
 execute cglib proxy: 37 ms



total repeat count is :3 current is : 3
 Create orginal: 6 ms
 execute orginal: 3 ms

 Create static proxy: 10 ms
 execute static proxy: 5 ms

 Create jdk proxy: 1351 ms
 execute jdk proxy: 25 ms

 Create cglib proxy: 2706 ms
 execute cglib proxy: 23 ms



do cglib proxy create 3 't, average timeCost is : 2780
do cglib proxy execute 3 't, average timeCost is : 27

do jdk proxy create 3 't, average timeCost is : 1313
do jdk proxy execute 3 't, average timeCost is : 36

do orginal create 3 't, average timeCost is : 9
do orginal execute 3 't, average timeCost is : 3

do static proxy create 3 't, average timeCost is : 20
do static proxy execute 3 't, average timeCost is : 5





deep test. run Count is :5000000
total repeat count is :3 current is : 1
 Create orginal: 39 ms
 execute orginal: 17 ms

 Create static proxy: 44 ms
 execute static proxy: 26 ms

 Create jdk proxy: 6210 ms
 execute jdk proxy: 107 ms

 Create cglib proxy: 12833 ms
 execute cglib proxy: 105 ms



total repeat count is :3 current is : 2
 Create orginal: 36 ms
 execute orginal: 18 ms

 Create static proxy: 45 ms
 execute static proxy: 30 ms

 Create jdk proxy: 6276 ms
 execute jdk proxy: 103 ms

 Create cglib proxy: 13323 ms
 execute cglib proxy: 110 ms



total repeat count is :3 current is : 3
 Create orginal: 41 ms
 execute orginal: 21 ms

 Create static proxy: 59 ms
 execute static proxy: 21 ms

 Create jdk proxy: 6433 ms
 execute jdk proxy: 108 ms

 Create cglib proxy: 12842 ms
 execute cglib proxy: 111 ms



do cglib proxy create 3 't, average timeCost is : 12999
do cglib proxy execute 3 't, average timeCost is : 108

do jdk proxy create 3 't, average timeCost is : 6306
do jdk proxy execute 3 't, average timeCost is : 106

do orginal create 3 't, average timeCost is : 38
do orginal execute 3 't, average timeCost is : 18

do static proxy create 3 't, average timeCost is : 49
do static proxy execute 3 't, average timeCost is : 25

通过比较,我们得出以下结论:

  • 在单例模式下。JDK动态代理与CGLIB动态代理性能相差不大
  • 在多例模式下。JDK动态代理的性能远大于CGLIB动态代理
  • JDK1.7JDK动态代理性能较于JDK1.6有明显的提升

深入背后的原因是:

  • JDK在创建代理(生成字节码时)效率远大于CGLIB
  • JDK动态代理执行与CGLIB动态代理执行的效率相差无几

##Spring在使用动态代理模式的策略

经过上述的实践考究,不难理解Spring为何如此使用代理模式时的策略。

那么Spring的策略是:

  • 如果目标对象实现了接口,默认情况会采用jdk的动态代理来实现AOP
  • 如果目标对象实现了接口,也可通过配置强制使用cglib实现AOP
  • 如果目标对象没有实现接口,必须采用cglib

后记

本文是在博主生日当天码的。有点心塞。

坚持原创技术分享,您的支持将鼓励我继续创作!