一、代理模式是什么
代理模式是一種設(shè)計(jì)模式,簡(jiǎn)單說(shuō)即是在不改變?cè)创a的情況下,實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的功能擴(kuò)展。
比如有個(gè)歌手對(duì)象叫Singer,這個(gè)對(duì)象有一個(gè)唱歌方法叫sing()。
1
2
3
4
5
|
1 public class Singer{ 2 public void sing(){ 3 System.out.println( "唱一首歌" ); 4 } 5 } |
假如你希望,通過(guò)你的某種方式生產(chǎn)出來(lái)的歌手對(duì)象,在唱歌前后還要想觀眾問(wèn)好和答謝,也即對(duì)目標(biāo)對(duì)象
Singer的sing方法進(jìn)行功能擴(kuò)展
。
1
2
3
4
5
|
1 public void sing(){ 2 System.out.println( "向觀眾問(wèn)好" ); 3 System.out.println( "唱一首歌" ); 4 System.out.println( "謝謝大家" ); 5 } |
但是往往你又不能直接對(duì)源代碼進(jìn)行修改
,可能是你希望原來(lái)的對(duì)象還保持原來(lái)的樣子,又或許你提供的只是一個(gè)可插拔的插件,甚至你有可能都不知道你要對(duì)哪個(gè)目標(biāo)對(duì)象進(jìn)行擴(kuò)展。這時(shí)就需要用到j(luò)ava的代理模式了。網(wǎng)上好多用生活中的經(jīng)理人的例子來(lái)解釋“代理”,看似通俗易懂,但我覺(jué)得不適合程序員去理解。程序員應(yīng)該從代碼的本質(zhì)入手。
二、Java的三種代理模式
想要實(shí)現(xiàn)以上的需求有三種方式,這一部分我們只看三種模式的代碼怎么寫(xiě),先不涉及實(shí)現(xiàn)原理的部分。
1.靜態(tài)代理
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
|
1 public interface ISinger { 2 void sing(); 3 } 4 5 /** 6 * 目標(biāo)對(duì)象實(shí)現(xiàn)了某一接口 7 */ 8 public class Singer implements ISinger{ 9 public void sing(){ 10 System.out.println( "唱一首歌" ); 11 } 12 } 13 14 /** 15 * 代理對(duì)象和目標(biāo)對(duì)象實(shí)現(xiàn)相同的接口 16 */ 17 public class SingerProxy implements ISinger{ 18 // 接收目標(biāo)對(duì)象,以便調(diào)用sing方法 19 private ISinger target; 20 public UserDaoProxy(ISinger target){ 21 this .target=target; 22 } 23 // 對(duì)目標(biāo)對(duì)象的sing方法進(jìn)行功能擴(kuò)展 24 public void sing() { 25 System.out.println( "向觀眾問(wèn)好" ); 26 target.sing(); 27 System.out.println( "謝謝大家" ); 28 } 29 } |
測(cè)試
1
2
3
4
5
6
7
8
9
10
11
12
13
|
1 /** 2 * 測(cè)試類 3 */ 4 public class Test { 5 public static void main(String[] args) { 6 //目標(biāo)對(duì)象 7 ISinger target = new Singer(); 8 //代理對(duì)象 9 ISinger proxy = new SingerProxy(target); 10 //執(zhí)行的是代理的方法 11 proxy.sing(); 12 } 13 } |
總結(jié):其實(shí)這里做的事情無(wú)非就是,創(chuàng)建一個(gè)代理類SingerProxy,繼承了ISinger接口并實(shí)現(xiàn)了其中的方法。只不過(guò)這種實(shí)現(xiàn)特意包含了目標(biāo)對(duì)象的方法,正是這種特征使得看起來(lái)像是“擴(kuò)展”了目標(biāo)對(duì)象的方法。假使代理對(duì)象中只是簡(jiǎn)單地對(duì)sing方法做了另一種實(shí)現(xiàn)而沒(méi)有包含目標(biāo)對(duì)象的方法,也就不能算作代理模式了。所以這里的包含是關(guān)鍵。
缺點(diǎn):這種實(shí)現(xiàn)方式很直觀也很簡(jiǎn)單,但其缺點(diǎn)是代理對(duì)象必須提前寫(xiě)出,如果接口層發(fā)生了變化,代理對(duì)象的代碼也要進(jìn)行維護(hù)。如果能在運(yùn)行時(shí)動(dòng)態(tài)地寫(xiě)出代理對(duì)象,不但減少了一大批代理類的代碼,也少了不斷維護(hù)的煩惱,不過(guò)運(yùn)行時(shí)的效率必定受到影響。這種方式就是接下來(lái)的動(dòng)態(tài)代理。
2.動(dòng)態(tài)代理(也叫JDK代理)
跟靜態(tài)代理的前提一樣,依然是對(duì)Singer對(duì)象進(jìn)行擴(kuò)展
1
2
3
4
5
6
7
8
9
10
11
12
|
1 public interface ISinger { 2 void sing(); 3 } 4 5 /** 6 * 目標(biāo)對(duì)象實(shí)現(xiàn)了某一接口 7 */ 8 public class Singer implements ISinger{ 9 public void sing(){ 10 System.out.println( "唱一首歌" ); 11 } 12 } |
這回直接上測(cè)試,由于java底層封裝了實(shí)現(xiàn)細(xì)節(jié)(之后會(huì)詳細(xì)講),所以代碼非常簡(jiǎn)單,格式也基本上固定。
調(diào)用Proxy類的靜態(tài)方法newProxyInstance即可,該方法會(huì)返回代理類對(duì)象
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
接收的三個(gè)參數(shù)依次為:
- ClassLoader loader:指定當(dāng)前目標(biāo)對(duì)象使用類加載器,寫(xiě)法固定
- Class<?>[] interfaces:目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型,寫(xiě)法固定
- InvocationHandler h:事件處理接口,需傳入一個(gè)實(shí)現(xiàn)類,一般直接使用匿名內(nèi)部類
測(cè)試代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
1 public class Test{ 2 public static void main(String[] args) { 3 Singer target = new Singer(); 4 ISinger proxy = (ISinger) Proxy.newProxyInstance( 5 target.getClass().getClassLoader(), 6 target.getClass().getInterfaces(), 7 new InvocationHandler() { 8 @Override 9 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 10 System.out.println( "向觀眾問(wèn)好" ); 11 //執(zhí)行目標(biāo)對(duì)象方法 12 Object returnValue = method.invoke(target, args); 13 System.out.println( "謝謝大家" ); 14 return returnValue; 15 } 16 }); 17 proxy.sing(); 18 } 19 } |
總結(jié):以上代碼只有標(biāo)黃的部分是需要自己寫(xiě)出,其余部分全都是固定代碼。由于java封裝了newProxyInstance這個(gè)方法的實(shí)現(xiàn)細(xì)節(jié),所以使用起來(lái)才能這么方便,具體的底層原理將會(huì)在下一小節(jié)說(shuō)明。
缺點(diǎn):可以看出靜態(tài)代理和JDK代理有一個(gè)共同的缺點(diǎn),就是目標(biāo)對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口,加入沒(méi)有,則可以使用Cglib代理。
3.Cglib代理
前提條件:
- 需要引入cglib的jar文件,由于Spring的核心包中已經(jīng)包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar
- 目標(biāo)類不能為final
- 目標(biāo)對(duì)象的方法如果為final/static,那么就不會(huì)被攔截,即不會(huì)執(zhí)行目標(biāo)對(duì)象額外的業(yè)務(wù)方法
1
2
3
4
5
6
7
8
9
|
1 /** 2 * 目標(biāo)對(duì)象,沒(méi)有實(shí)現(xiàn)任何接口 3 */ 4 public class Singer{ 5 6 public void sing() { 7 System.out.println( "唱一首歌" ); 8 } 9 } |
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
|
1 /** 2 * Cglib子類代理工廠 3 */ 4 public class ProxyFactory implements MethodInterceptor{ 5 // 維護(hù)目標(biāo)對(duì)象 6 private Object target; 7 8 public ProxyFactory(Object target) { 9 this .target = target; 10 } 11 12 // 給目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象 13 public Object getProxyInstance(){ 14 //1.工具類 15 Enhancer en = new Enhancer(); 16 //2.設(shè)置父類 17 en.setSuperclass(target.getClass()); 18 //3.設(shè)置回調(diào)函數(shù) 19 en.setCallback( this ); 20 //4.創(chuàng)建子類(代理對(duì)象) 21 return en.create(); 22 } 23 24 @Override 25 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 26 System.out.println( "向觀眾問(wèn)好" ); 27 //執(zhí)行目標(biāo)對(duì)象的方法 28 Object returnValue = method.invoke(target, args); 29 System.out.println( "謝謝大家" ); 30 return returnValue; 31 } 32 } |
這里的代碼也非常固定,只有標(biāo)黃部分是需要自己寫(xiě)出
測(cè)試
1
2
3
4
5
6
7
8
9
10
11
12
13
|
1 /** 2 * 測(cè)試類 3 */ 4 public class Test{ 5 public static void main(String[] args){ 6 //目標(biāo)對(duì)象 7 Singer target = new Singer(); 8 //代理對(duì)象 9 Singer proxy = (Singer) new ProxyFactory(target).getProxyInstance(); 10 //執(zhí)行代理對(duì)象的方法 11 proxy.sing(); 12 } 13 } |
總結(jié):三種代理模式各有優(yōu)缺點(diǎn)和相應(yīng)的適用范圍,主要看目標(biāo)對(duì)象是否實(shí)現(xiàn)了接口。以Spring框架所選擇的代理模式舉例
- 在Spring的AOP編程中:
- 如果加入容器的目標(biāo)對(duì)象有實(shí)現(xiàn)接口,用JDK代理
- 如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)接口,用Cglib代理
到此這篇關(guān)于Java的三種代理模式簡(jiǎn)述的文章就介紹到這了,更多相關(guān)Java的三種代理模式內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://www.cnblogs.com/flashsun/p/7286475.html