Java代理(靜態代理、JDK動態代理、CGLIB動態代理)

Java中代理有靜態代理動態代理。靜態代理的代理關係在編譯時就肯定了,而動態代理的代理關係是在運行期肯定的。靜態代理實現簡單,適合於代理類較少且肯定的狀況,而動態代理則給咱們提供了更大的靈活性。html

Java中動態代理有JDK原生動態代理CGLIB動態代理兩種。前者本質上是根據定好的接口動態生成靜態代理類(該接口的實現類);後者則不須要事先定好接口而是能夠直接根據類進行動態代理,其本質是根據指定的類動態生成靜態代理類(指定的類的子類)。動態代理中,被代理的對象的全部方法都會被代理,除非在代理邏輯中進行方法篩選。java

本質spring

JDK原生動態代理:生成被代理對象所實現的接口的實現類;實現類中每一個方法調用被代理對象的相應方法,只不過在調用先後加上了額外邏輯;要求被代理對象實現接口ide

CGLIB動態代理:生成被代理對象的子類;子類中的每一個方法調用被代理對象(父類對象)中的相應方法,只不過在調用先後加上了額外處理;要求被代理對象不能被final修飾測試

 

動態代理是IOC、AOP等技術的基礎。ui

 

靜態代理和JDK動態代理

Dynamic proxies allow one single class with one single method to service multiple method calls to arbitrary classes with an arbitrary number of methods. A dynamic proxy can be thought of as a kind of Facade, but one that can pretend to be an implementation of any interface. Under the cover, it routes all method invocations to a single handler – the invoke() method.   https://www.baeldung.com/java-dynamic-proxiesthis

示例:spa

1 package com.marchon.proxytest;
2 
3 public interface IUserService {
4     public String getUserName();
5     public Integer getAge(String userName);
6 }
IUserService
 1 package com.marchon.proxytest;
 2 
 3 /**
 4  * 被代理對象
 5  * 
 6  * @author zsm
 7  *
 8  */
 9 public class UserServiceImpl implements IUserService {
10 
11     @Override
12     public String getUserName() {
13         String res = this.getClass() + ":hello";
14         System.out.println(res);
15         return res;
16     }
17 
18     @Override
19     public Integer getAge(String userName) {
20         Integer age = 20;
21         System.out.println(age);
22         return age;
23     }
24 
25 }
UserServiceImpl
 1 package com.marchon.proxytest;
 2 
 3 /**
 4  * 代理對象(靜態代理)<br>
 5  * 缺點:<br>
 6  * 一、代理類和被代理類實現相同的接口,代碼重複、得爲每一個接口都實現相應的實現從而維護成本高 二、代理對象只服務於被代理對象,即每一個被代理對象都得實現相應的代理對象
 7  * 
 8  * @author zsm
 9  *
10  */
11 class UserServiceStaticProxy implements IUserService {
12     private IUserService proxiedObj;
13 
14     public UserServiceStaticProxy(IUserService proxiedObj) {
15         if (proxiedObj instanceof UserServiceStaticProxy) {
16             throw new RuntimeException("illegal proxiedObj proxied object");
17         }
18 
19         this.proxiedObj = proxiedObj;
20     }
21 
22     @Override
23     public String getUserName() {
24         System.out.println("before");
25         String res = proxiedObj.getUserName();
26         System.out.println("after");
27         return res;
28 
29     }
30 
31     @Override
32     public Integer getAge(String userName) {
33         System.out.println("before");
34         Integer age = proxiedObj.getAge(userName);
35         System.out.println("after");
36         return age;
37     }
38 
39 }
40 
41 public class Main_StaticProxy {
42     public static void main(String[] args) {
43         IUserService proxiedObj = new UserServiceImpl();
44 
45         // UserServiceStaticProxy proxy = new UserServiceStaticProxy(proxiedObj);
46         IUserService proxyObject = new UserServiceStaticProxy(proxiedObj);
47         proxyObject.getUserName();
48         proxyObject.getAge("zhangsan");
49     }
50 }
StaticProxy  
  1 package com.marchon.proxytest;
  2 
  3 import java.io.FileOutputStream;
  4 import java.io.IOException;
  5 import java.lang.reflect.InvocationHandler;
  6 import java.lang.reflect.Method;
  7 import java.lang.reflect.Proxy;
  8 
  9 import sun.misc.ProxyGenerator;
 10 
 11 /**
 12  * 代理對象(動態代理)<br>
 13  * 動態代理:在程序運行期間根據須要動態建立代理類及其實例來完成具體的功能。動態代理主要分爲JDK動態代理和cglib動態代理兩大類<br>
 14  * 這裏介紹jdk動態代理,其本質上是在運行時動態產生一個實現指定接口的靜態代理類,指定接口的全部非final方法(包括繼承的非final方法如toString)均會被代理。
 15  * 
 16  * @author zsm
 17  *
 18  */
 19 
 20 class JdkDynamicProxyTemplate implements InvocationHandler {
 21     private Object proxiedObj;
 22 
 23     public JdkDynamicProxyTemplate(Object proxiedObj) {
 24         this.proxiedObj = proxiedObj;
 25     }
 26 
 27     @Override
 28     public Object invoke(Object proxyObj, Method method, Object[] args) throws Throwable {
 29         // if (method.getName().equals("getUserName")) {//爲避免全部非final方法都被代理,可做此判斷
 30         //
 31         // }
 32 
 33         // System.out.println(proxyObj);//stack overflow, why?
 34         // System.out.println(method);// public abstract java.lang.String com.marchon.proxytest.IUserService.getUserName()
 35 
 36         System.out.println("before");
 37         Object res = method.invoke(proxiedObj, args);
 38         System.out.println("after");
 39 
 40         return res;
 41     }
 42 
 43 }
 44 
 45 public class Main_JdkDynamicProxy {// 參閱:https://www.jianshu.com/p/269afd0a52e6
 46     public static void main(String[] args) {
 47         IUserService proxiedObj = new UserServiceImpl();
 48 
 49         JdkDynamicProxyTemplate proxyTemplate = new JdkDynamicProxyTemplate(proxiedObj);
 50 
 51         // 第一個參數是指定代理類的類加載器(咱們傳入當前測試類的類加載器)
 52         // 第二個參數是代理類須要實現的接口(咱們傳入被代理類實現的接口,這樣生成的代理類和被代理類就實現了相同的接口)
 53         // 第三個參數是invocation handler,用來處理方法的調用。這裏傳入咱們本身實現的handler
 54         IUserService proxyObject = (IUserService) Proxy.newProxyInstance(proxiedObj.getClass().getClassLoader(),
 55                 proxiedObj.getClass().getInterfaces(), proxyTemplate);// 建立包含被代理對象各方法的代理對象,顯然可知:該代理對象實現了所傳接口中(這裏爲IUserService)定義的各方法、代理對象的各方法實現爲直接調用proxyTemplate相應的各方法實現。可使用sum.misc下的ProxyGenerator生成動態代理類的字節碼文件,再反編譯出動態代理類源碼
 56 
 57         proxyObject.getUserName();
 58         proxyObject.getAge("zhangsan");
 59         proxyObject.toString();// 全部非final方法都會被代理,包括從Object繼承的等
 60 
 61         {
 62             // 獲取代理類字節碼文件
 63             String path = "$Proxy0.class";
 64             byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", proxiedObj.getClass().getInterfaces());
 65             FileOutputStream out = null;
 66 
 67             try {
 68                 out = new FileOutputStream(path);
 69                 out.write(classFile);
 70                 out.flush();
 71             } catch (Exception e) {
 72                 e.printStackTrace();
 73             } finally {
 74                 try {
 75                     out.close();
 76                 } catch (IOException e) {
 77                     e.printStackTrace();
 78                 }
 79             }
 80         }
 81 
 82         // jdk動態代理動態產生的代理類相似於以下靜態代理
 83         System.out.println();
 84         System.out.println("equivalent static proxy test:");
 85         new TmpEquivalentStaticProxy(proxyTemplate).getUserName();
 86 
 87     }
 88 }
 89 
 90 /**
 91  * 
 92  * 上述生成的動態代理生成的代理類實際是相似於本類
 93  * 
 94  * @author zsm
 95  *
 96  */
 97 class TmpEquivalentStaticProxy implements IUserService {// 真正由jdk動態代理生成的代理類還 extends Proxy
 98     private InvocationHandler invokeHandler;
 99 
100     public TmpEquivalentStaticProxy(InvocationHandler invokeHandler) {
101         this.invokeHandler = invokeHandler;
102     }
103 
104     @Override
105     public String getUserName() {
106         Object[] args = null;
107         Method getUserNameMethod = IUserService.class.getMethods()[0];// public abstract java.lang.String
108                                                                         // com.marchon.proxytest.IUserService.getUserName()
109         try {
110             return (String) invokeHandler.invoke(this, getUserNameMethod, args);
111         } catch (Throwable e) {
112             e.printStackTrace();
113             return null;
114         }
115 
116     }
117 
118     @Override
119     public Integer getAge(String userName) {
120         Object[] args = new Object[] { userName };
121         try {
122             Method getAgeMethod = IUserService.class.getMethods()[1];// public abstract java.lang.Integer
123                                                                         // com.marchon.proxytest.IUserService.getAge(java.lang.String)
124             return (Integer) invokeHandler.invoke(this, getAgeMethod, args);
125         } catch (Throwable e) {
126             e.printStackTrace();
127             return null;
128         }
129 
130     }
131 
132 }
JdkDynamic

 

 

 

由上可見,靜態代理的各個方法具備共性:每一個方法內的先後都作額外其餘處理、中間調用被代理對象的相應方法。既然有共性,那就能夠抽取共性以減小重複,也即將靜態代理的方法抽象出一個」模板「,這樣就不須要在代理對象中針對被代理對象的每一個方法寫額外邏輯,這其實就變成了動態代理。.net

JDK動態代理其實是自動生成一個靜態代理類並建立相應實例。代理類默認繼承Porxy類,由於Java中只支持單繼承,因此JDK動態代理只能去實現接口;代理類的方法都會去調用InvocationHandler的invoke()方法,故此需重寫InvocationHandler的invoke()方法。代理

JDK動態代理爲咱們提供了很是靈活的代理機制,但也有不足:

被代理對象的全部非final方法(如從Object繼承的toString、equals等)都會被代理(即都會在方法先後作與InvocationHandler的invoke()方法先後一樣的處理,固然咱們能夠經過在invoke裏對method name加以判斷避免此狀況),然而有時候咱們並不但願這些方法被代理。

JDK動態代理是基於接口的(生成的動態代理類實際上extends Proxy implements IUserService)。若是要被代理的對象沒有實現接口,該如何實現代理呢?可用下面要介紹的CGLIB動態代理。

從上面討論可得知,靜態代理和動態代理在運行時是同樣的(都是靜態代理了),其差別在運行時以前(即編譯期)才存在,差別體如今開發者是否須要分別在每一個與被代理方法對應的代理方法的先後寫額外的處理邏輯:靜態代理須要而動態代理不須要(後者不須要是由於該任務從由開發者負責轉交由JVM來負責了)。所以,動態代理相比於靜態代理的主要優點是爲開發者提供便利,不用像靜態代理那寫重複的代理方法邏輯。

 

CGLIB動態代理(springframework)

與JDK動態代理不一樣,CGLIB(Code Generation Library)動態代理不須要事先定義接口,而是能夠直接對類進行動態代理。CGLIB動態代理中被代理對象的全部非final方法默認也會被代理。

示例:

 1 package com.marchon.proxytest;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 import org.springframework.cglib.proxy.Enhancer;
 6 import org.springframework.cglib.proxy.MethodInterceptor;
 7 import org.springframework.cglib.proxy.MethodProxy;
 8 
 9 /**
10  * 代理對象(動態代理)<br>
11  * 動態代理:在程序運行期間根據須要動態建立代理類及其實例來完成具體的功能。動態代理主要分爲JDK動態代理和cglib動態代理兩大類<br>
12  * 這裏介紹cglib動態代理,其本質上是在運行時動態產生一個實現繼承指定類的的靜態代理類,指定類的全部非final方法(包括繼承的非final方法如toString)均會被代理。
13  * 
14  * @author zsm
15  *
16  */
17 
18 class CglibDynamicProxyTemplate implements MethodInterceptor {
19 
20     @Override
21     public Object intercept(Object proxyObj, Method proxiedMethod, Object[] args, MethodProxy proxyMethod) throws Throwable {
22         // if (method.getName().equals("sayHello")) {//爲避免全部非final方法都被代理,可做此判斷
23         //
24         // }
25 
26         // System.out.println(proxyObj);//stack overflow, why?
27         // System.out.println(proxiedMethod);// public java.lang.String
28         // com.marchon.proxytest.HelloConcrete.sayHello(java.lang.String)
29         // System.out.println(proxyMethod);// org.springframework.cglib.proxy.MethodProxy@108c4c35
30 
31         System.out.println("before");
32         Object res = proxyMethod.invokeSuper(proxyObj, args);
33         System.out.println("after");
34         return res;
35     }
36 
37 }
38 
39 class HelloConcrete {
40     public String sayHello(String username) {
41         return "hello " + username;
42     }
43 
44     public final String getAddress() {
45         return "beijing";
46     }
47 }
48 
49 public class Main_CglibDynamicProxy {
50     public static void main(String[] args) {
51         CglibDynamicProxyTemplate proxyTemplate = new CglibDynamicProxyTemplate();
52 
53         Enhancer enhancer = new Enhancer();
54         enhancer.setSuperclass(HelloConcrete.class);
55         enhancer.setCallback(proxyTemplate);
56 
57         HelloConcrete proxyObject = (HelloConcrete) enhancer.create();
58         System.out.println(proxyObject.sayHello("zhangsan"));// 會建立包含被代理對象全部非final方法的代理類,實際上代理類是被代理類的子類,故不會代理final方法;代理對象的各方法實現爲直接調用proxyTemplate相應的各方法實現
59         proxyObject.hashCode();// 全部非final方法都會被代理,包括從Object繼承的等
60         proxyObject.getAddress();// final方法不會被代理
61 
62         // jdk動態代理動態產生的代理類相似於以下靜態代理
63         System.out.println();
64         System.out.println("equivalent static proxy test:");
65         new TmpEquivalentStaticProxyOfCglib(proxyTemplate).sayHello("zhangsan");
66     }
67 }
68 
69 class TmpEquivalentStaticProxyOfCglib extends HelloConcrete {// 真正由cglib動態代理生成的代理類還 implements Factory
70     private MethodInterceptor methodInterceptor;
71 
72     public TmpEquivalentStaticProxyOfCglib(MethodInterceptor methodInterceptor) {
73         this.methodInterceptor = methodInterceptor;
74     }
75 
76     @Override
77     public String sayHello(String username) {
78         Object[] args = null;
79         Method proxiedMethod = super.getClass().getMethods()[0];
80         MethodProxy proxyMethod = null;// this.getClass().getMethods()[0];//不造如何獲取MethodProxy對象,故此方法實際上跑不了
81 
82         try {
83             return (String) methodInterceptor.intercept(this, proxiedMethod, args, proxyMethod);
84         } catch (Throwable e) {
85             e.printStackTrace();
86             return null;
87         }
88 
89     }
90 
91     // 沒法override getAddress方法
92 }
CglibDynamicProxy

由上可見,此動態代理本質上是在運行時動態根據指定的類繼承實現一個子類(故指定的類不能是final的),在子類中重寫方法,方法的實現爲調用MethodInterceptor中的intercept方法。

 

 

參考資料

https://www.jianshu.com/p/269afd0a52e6

http://www.javashuo.com/article/p-cmwrofsp-kg.html

https://www.cnblogs.com/CarpenterLee/p/8241042.html JDK動態代理和CGLIB動態代理

https://blog.csdn.net/difffate/article/details/70552056 CGLIB動態代理 

相關文章
相關標籤/搜索