1. 程式人生 > 其它 >06代理模式

06代理模式

一、代理模式定義

1.代理模式是指為其他物件提供一種代理,以控制對這個物件的訪問,屬於結構型模式。

2.在某些情況下,一個物件不適合或者不能直接引用另一個物件,而代理物件可以在客戶端和目標物件之間起到中介的作用。

3.代理模式一般包含三種角色:

  A.抽象主題角色(Subject):抽象主題類的主要職責是宣告真是主題與代理的共同介面方法,該類可以是介面也可以是抽象類

  B.真實主題角色(RealSubject):該類也被稱為被代理類,該類定義了代理所表示的真實物件,是負責執行系統真正的業務邏輯物件

  C.代理主題角色(Proxy):也被稱為代理類,其內部持有RealSubject的引用,因此具備完全的對RealSubject的代理權。客戶端呼叫代理物件的方法,同時也呼叫被代理物件的方法,但是會在代理物件前後增加      一些處理程式碼

4.在程式碼中,一般代理會被理解為程式碼增強,實際上就是在原始碼邏輯前後增加一些程式碼邏輯,而使呼叫者無感知。代理模式屬於結構型模式,分為靜態代理和動態代理

5.代理模式的目的:一是保護目標物件,二是增強目標物件

二、代理模式的通用寫法

1.程式碼示例:

 1 /**
 2  * 代理主題角色
 3  */
 4 public interface ISubject {
 5     void request();
 6 }
 7 
 8 /**
 9  * 真實主題角色
10  */
11 public class RealSubject implements ISubject {
12     @Override
13 public void request() { 14 System.out.println("real service is called."); 15 } 16 } 17 18 /** 19 * 代理主題角色 20 */ 21 public class Proxy implements ISubject { 22 23 private ISubject subject; 24 25 public Proxy(ISubject subject){ 26 this.subject = subject; 27 } 28
29 @Override 30 public void request() { 31 before(); 32 subject.request(); 33 after(); 34 } 35 36 public void before(){ 37 System.out.println("called before request()."); 38 } 39 40 public void after(){ 41 System.out.println("called after request()."); 42 } 43 } 44 45 public class GeneralProxyTest { 46 public static void main(String[] args) { 47 Proxy proxy = new Proxy(new RealSubject()); 48 proxy.request(); 49 } 50 }

三、動態代理

1.上面通用寫法實際上是一種靜態代理,動態代理分為jdk動態代理和cglib動態代理

2.jdk動態代理示例

 1 public interface IPerson {
 2     void findLove();
 3 }
 4 
 5 public class ZhangSan implements IPerson {
 6     @Override
 7     public void findLove() {
 8         System.out.println("膚白貌美大長腿");
 9     }
10 }
11 
12 public class JdkMeiPo implements InvocationHandler {
13 
14     private IPerson target;
15 
16     public IPerson getInstance(IPerson target){
17         this.target = target;
18         Class<? extends IPerson> clazz = target.getClass();
19         return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
20     }
21 
22     @Override
23     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
24         before();
25         Object result = method.invoke(this.target, args);
26         after();
27         return result;
28     }
29 
30     private void before(){
31         System.out.println("我是媒婆:已經收集到你的需求,開始物色");
32     }
33 
34     private void after(){
35         System.out.println("雙方同意,開始交往");
36     }
37 }
38 
39 public class JdkDynamicProxyTest {
40     public static void main(String[] args) {
41         JdkMeiPo jdkMeiPo = new JdkMeiPo();
42         IPerson instance = jdkMeiPo.getInstance(new ZhangSan());
43         instance.findLove();
44     }
45 
46 }

A.jdk動態代理採用位元組碼重組,重新生成物件來替代原始物件,以達到動態代理的目的。繼承Proxy類並實現被代理類的所有介面,重寫方法

B.jdk動態代理生成物件步驟:

  a.獲取被代理物件的引用,並獲取它的所有介面,反射獲取

  b.jdk動態代理類重新生成一個新的類,同時新的類要實現被代理類實現的所有介面

  c.動態生成java程式碼,新加的業務邏輯方法由一定的邏輯程式碼呼叫

  d.編譯新生成的java程式碼為.class檔案

  e.重新載入到jvm執行

3.cglib動態程式碼示例

 1 public class Customer {
 2     public void findLove(){
 3         System.out.println("有車有房有存款");
 4     }
 5 }
 6 
 7 public class CglibMeiPo implements MethodInterceptor {
 8 
 9     public Object getInstance(Class<?> clazz) throws Exception{
10         Enhancer enhancer = new Enhancer();
11         enhancer.setSuperclass(clazz);
12         enhancer.setCallback(this);
13         return enhancer.create();
14     }
15 
16     @Override
17     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
18         before();
19         Object obj = methodProxy.invokeSuper(o,objects);
20         after();
21         return obj;
22     }
23 
24     private void before(){
25         System.out.println("我是媒婆:我要給你找物件,現在已經確認你的需求");
26         System.out.println("開始物色");
27     }
28 
29     private void after(){
30         System.out.println("如果合適的話,就準備辦事");
31     }
32 
33 }

A.cglib代理的目標物件不需要實現介面,它是通過動態繼承目標物件實現動態代理的

B.cglib會生成三個.class:繼承目標類的,代理和被代理的FastClass

C.FastClass機制:為代理類和被代理類各生成一個類,這個類會為代理類或被代理類的方法分配一個index(int型別);這個index當作一個入參,FastClass就可以直接定位要呼叫的方法並直接進行呼叫,省去了反射呼叫,所以呼叫效率比JDK代理高

4.Cglib和Jdk動態代理對比

  A.jdk動態代理實現了被代理物件的介面,cglib代理繼承了被代理物件

  B.jdk動態代理和cglib代理都在執行期生成位元組碼,jdk動態代理直接寫Class位元組碼,cglib代理使用ASM框架寫Class位元組碼,cglib代理實現更復雜,生成代理類效率比jdk動態代理低

  C.jdk動態代理呼叫代理方法是通過反射機制呼叫的,cglib代理是通過FastClass機制直接呼叫方法的,cglib代理的執行效率更高

四、靜態代理和動態代理的本質區別

1.靜態代理只能通過手動完成代理操作,如果被代理類增加了新的方法,代理類需要同步增加,違反了開閉原則

2.動態代理採用在執行時生成程式碼的方式,取消了對被代理類的擴充套件限制,遵循開閉原則

3.若動態代理要對目標類的增強邏輯進行擴充套件,結合策略模式,只需要新增策略類即可,無須修改代理類的程式碼

五、代理模式的優缺點

1.優點:

  A.代理模式能將代理物件與真實被呼叫目標物件分離

  B.在一定程度上降低了系統的耦合性,擴充套件性好

  C.可以起到保護目標物件的作用

  D.可以增強目標物件的功能

2.缺點:

  A.代理模式會造成系統設計中類的數量增加

  B.在客戶端和目標物件中增加一個代理物件,會導致請求處理速度變慢

  C.增加了系統複雜度