代理模式及实现探究
前言
有使用過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 :1000000total repeat count is :3 current is : 1execute orginal : 32 msexecute static proxy : 60 msexecute jdk proxy : 48 msexecute cglib proxy : 23 mstotal repeat count is :3 current is : 2execute orginal : 8 msexecute static proxy : 7 msexecute jdk proxy : 22 msexecute cglib proxy : 33 mstotal repeat count is :3 current is : 3execute orginal : 4 msexecute static proxy : 4 msexecute jdk proxy : 20 msexecute cglib proxy : 18 msexecute :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 : 30test runCount :5000000total repeat count is :3 current is : 1execute orginal : 21 msexecute static proxy : 25 msexecute jdk proxy : 129 msexecute cglib proxy : 123 mstotal repeat count is :3 current is : 2execute orginal : 20 msexecute static proxy : 21 msexecute jdk proxy : 141 msexecute cglib proxy : 92 mstotal repeat count is :3 current is : 3execute orginal : 22 msexecute static proxy : 20 msexecute jdk proxy : 145 msexecute cglib proxy : 123 msexecute :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 :1000000total repeat count is :3 current is : 1execute orginal : 25 msexecute static proxy : 40 msexecute jdk proxy : 658 msexecute cglib proxy : 3197 mstotal repeat count is :3 current is : 2execute orginal : 15 msexecute static proxy : 19 msexecute jdk proxy : 1315 msexecute cglib proxy : 3852 mstotal repeat count is :3 current is : 3execute orginal : 15 msexecute static proxy : 17 msexecute jdk proxy : 799 msexecute cglib proxy : 2935 msexecute :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 : 924test runCount :5000000total repeat count is :3 current is : 1execute orginal : 37 msexecute static proxy : 204 msexecute jdk proxy : 3348 msexecute cglib proxy : 14302 mstotal repeat count is :3 current is : 2execute orginal : 39 msexecute static proxy : 49 msexecute jdk proxy : 3154 msexecute cglib proxy : 14122 mstotal repeat count is :3 current is : 3execute orginal : 39 msexecute static proxy : 211 msexecute jdk proxy : 3255 msexecute cglib proxy : 13464 msexecute :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 : 1Create orginal: 15 msexecute orginal: 4 msCreate static proxy: 36 msexecute static proxy: 9 msCreate jdk proxy: 588 msexecute jdk proxy: 41 msCreate cglib proxy: 2830 msexecute cglib proxy: 210 mstotal repeat count is :3 current is : 2Create orginal: 5 msexecute orginal: 3 msCreate static proxy: 6 msexecute static proxy: 4 msCreate jdk proxy: 640 msexecute jdk proxy: 20 msCreate cglib proxy: 2759 msexecute cglib proxy: 14 mstotal repeat count is :3 current is : 3Create orginal: 3 msexecute orginal: 3 msCreate static proxy: 6 msexecute static proxy: 4 msCreate jdk proxy: 689 msexecute jdk proxy: 19 msCreate cglib proxy: 2684 msexecute cglib proxy: 14 msdo cglib proxy create 3 't, average timeCost is : 2757 do cglib proxy execute 3 't, average timeCost is : 79do jdk proxy create 3 't, average timeCost is : 639 do jdk proxy execute 3 't, average timeCost is : 26do orginal create 3 't, average timeCost is : 7 do orginal execute 3 't, average timeCost is : 3do static proxy create 3 't, average timeCost is : 16 do static proxy execute 3 't, average timeCost is : 5deep test. run Count is :5000000 total repeat count is :3 current is : 1Create orginal: 17 msexecute orginal: 16 msCreate static proxy: 37 msexecute static proxy: 21 msCreate jdk proxy: 2942 msexecute jdk proxy: 104 msCreate cglib proxy: 13697 msexecute cglib proxy: 74 mstotal repeat count is :3 current is : 2Create orginal: 96 msexecute orginal: 22 msCreate static proxy: 45 msexecute static proxy: 20 msCreate jdk proxy: 2927 msexecute jdk proxy: 117 msCreate cglib proxy: 14023 msexecute cglib proxy: 79 mstotal repeat count is :3 current is : 3Create orginal: 18 msexecute orginal: 21 msCreate static proxy: 29 msexecute static proxy: 18 msCreate jdk proxy: 2898 msexecute jdk proxy: 107 msCreate cglib proxy: 15810 msexecute cglib proxy: 210 msdo cglib proxy create 3 't, average timeCost is : 14510 do cglib proxy execute 3 't, average timeCost is : 121do jdk proxy create 3 't, average timeCost is : 2922 do jdk proxy execute 3 't, average timeCost is : 109do orginal create 3 't, average timeCost is : 43 do orginal execute 3 't, average timeCost is : 19do 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 :1000000total repeat count is :3 current is : 1execute orginal : 44 msexecute static proxy : 110 msexecute jdk proxy : 35 msexecute cglib proxy : 25 mstotal repeat count is :3 current is : 2execute orginal : 2 msexecute static proxy : 4 msexecute jdk proxy : 22 msexecute cglib proxy : 21 mstotal repeat count is :3 current is : 3execute orginal : 4 msexecute static proxy : 5 msexecute jdk proxy : 22 msexecute cglib proxy : 20 msexecute :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 : 26test runCount :5000000total repeat count is :3 current is : 1execute orginal : 19 msexecute static proxy : 21 msexecute jdk proxy : 110 msexecute cglib proxy : 111 mstotal repeat count is :3 current is : 2execute orginal : 17 msexecute static proxy : 20 msexecute jdk proxy : 108 msexecute cglib proxy : 112 mstotal repeat count is :3 current is : 3execute orginal : 19 msexecute static proxy : 20 msexecute jdk proxy : 106 msexecute cglib proxy : 109 msexecute :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 :1000000total repeat count is :3 current is : 1execute orginal : 20 msexecute static proxy : 31 msexecute jdk proxy : 1333 msexecute cglib proxy : 2790 mstotal repeat count is :3 current is : 2execute orginal : 11 msexecute static proxy : 13 msexecute jdk proxy : 1257 msexecute cglib proxy : 3907 mstotal repeat count is :3 current is : 3execute orginal : 19 msexecute static proxy : 18 msexecute jdk proxy : 2135 msexecute cglib proxy : 3424 msexecute :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 : 1575test runCount :5000000total repeat count is :3 current is : 1execute orginal : 77 msexecute static proxy : 97 msexecute jdk proxy : 7851 msexecute cglib proxy : 15401 mstotal repeat count is :3 current is : 2execute orginal : 49 msexecute static proxy : 60 msexecute jdk proxy : 8527 msexecute cglib proxy : 14931 mstotal repeat count is :3 current is : 3execute orginal : 92 msexecute static proxy : 106 msexecute jdk proxy : 7951 msexecute cglib proxy : 14286 msexecute :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 : 1Create orginal: 16 msexecute orginal: 5 msCreate static proxy: 40 msexecute static proxy: 6 msCreate jdk proxy: 1270 msexecute jdk proxy: 56 msCreate cglib proxy: 2679 msexecute cglib proxy: 23 mstotal repeat count is :3 current is : 2Create orginal: 7 msexecute orginal: 3 msCreate static proxy: 10 msexecute static proxy: 4 msCreate jdk proxy: 1320 msexecute jdk proxy: 27 msCreate cglib proxy: 2956 msexecute cglib proxy: 37 mstotal repeat count is :3 current is : 3Create orginal: 6 msexecute orginal: 3 msCreate static proxy: 10 msexecute static proxy: 5 msCreate jdk proxy: 1351 msexecute jdk proxy: 25 msCreate cglib proxy: 2706 msexecute cglib proxy: 23 msdo cglib proxy create 3 't, average timeCost is : 2780 do cglib proxy execute 3 't, average timeCost is : 27do jdk proxy create 3 't, average timeCost is : 1313 do jdk proxy execute 3 't, average timeCost is : 36do orginal create 3 't, average timeCost is : 9 do orginal execute 3 't, average timeCost is : 3do static proxy create 3 't, average timeCost is : 20 do static proxy execute 3 't, average timeCost is : 5deep test. run Count is :5000000 total repeat count is :3 current is : 1Create orginal: 39 msexecute orginal: 17 msCreate static proxy: 44 msexecute static proxy: 26 msCreate jdk proxy: 6210 msexecute jdk proxy: 107 msCreate cglib proxy: 12833 msexecute cglib proxy: 105 mstotal repeat count is :3 current is : 2Create orginal: 36 msexecute orginal: 18 msCreate static proxy: 45 msexecute static proxy: 30 msCreate jdk proxy: 6276 msexecute jdk proxy: 103 msCreate cglib proxy: 13323 msexecute cglib proxy: 110 mstotal repeat count is :3 current is : 3Create orginal: 41 msexecute orginal: 21 msCreate static proxy: 59 msexecute static proxy: 21 msCreate jdk proxy: 6433 msexecute jdk proxy: 108 msCreate cglib proxy: 12842 msexecute cglib proxy: 111 msdo cglib proxy create 3 't, average timeCost is : 12999 do cglib proxy execute 3 't, average timeCost is : 108do jdk proxy create 3 't, average timeCost is : 6306 do jdk proxy execute 3 't, average timeCost is : 106do orginal create 3 't, average timeCost is : 38 do orginal execute 3 't, average timeCost is : 18do 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/
總結
- 上一篇: 彻底理解JAVA动态代理
- 下一篇: 第 16 章 反射(Reflection