前言
有使用过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 { 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 */ public void saySomething(String something) { Date startDate = new Date(); System.out.println("开始调用目标类时时间点:" + startDate); // 将请求分派给委托类处理 testService.saySomething(something); Date endDate = new Date(); System.out.println("结束调用目标类时时间点:" + endDate); } 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.7
的JDK
动态代理性能较于JDK1.6
有明显的提升
深入背后的原因是:
JDK
在创建代理(生成字节码时)效率远大于CGLIB
JDK
动态代理执行与CGLIB
动态代理执行的效率相差无几
##Spring在使用动态代理模式的策略
经过上述的实践考究,不难理解Spring
为何如此使用代理模式时的策略。
那么Spring
的策略是:
- 如果目标对象实现了接口,默认情况会采用jdk的动态代理来实现AOP
- 如果目标对象实现了接口,也可通过配置强制使用cglib实现AOP
- 如果目标对象没有实现接口,必须采用cglib
后记
本文是在博主生日当天码的。有点心塞。
from: http://huangnx.com/2016/10/17/proxyDesignDesc/