設計模式之代理模式(結構型)

weixin_33860553發表於2019-01-19

第一章

1.1 模式定義

代理模式:代理模式就是引入一個代理物件,通過代理物件實現對原物件的引用。代理模式是一種物件結構型。

1.2 代理模式包含如下角色

  • Subject:抽象主題角色
  • Proxy:代理主題角色
  • RealSubject:真實主題角色

[圖片上傳失敗...(image-d1d75e-1547891819525)]

1.3 模式例子

public class Proxy implements Subject
{
    private RealSubject realSubject = new RealSubject();
    public void preRequest()
    {…...}
    public void request()
    {
        preRequest();
        realSubject.request();
        postRequest();
    }
    public void postRequest()
    {……}
} 

1.4 模式型別

來自:《設計模式》一書歸納分類

  • 遠端(Remote)代理:為一個位於不同的地址空間的物件提供一個本地的代理物件,這個不同的地址空間可以是在同一臺主機中,也可是在另一臺主機中,遠端代理又叫做大使(Ambassador)。
  • 虛擬(Virtual)代理:如果需要建立一個資源消耗較大的物件,先建立一個消耗相對較小的物件來表示,真實物件只在需要時才會被真正建立。
  • Copy-on-Write代理:它是虛擬代理的一種,把複製(克隆)操作延遲到只有在客戶端真正需要時才執行。一般來說,物件的深克隆是一個開銷較大的操作,Copy-on-Write代理可以讓這個操作延遲,只有物件被用到的時候才被克隆。
  • 保護(Protect or Access)代理:控制對一個物件的訪問,可以給不同的使用者提供不同級別的使用許可權。
  • 緩衝(Cache)代理:為某一個目標操作的結果提供臨時的儲存空間,以便多個客戶端可以共享這些結果。
  • 防火牆(Firewall)代理:保護目標不讓惡意使用者接近。
  • 同步化(Synchronization)代理:使幾個使用者能夠同時使用一個物件而沒有衝突。
  • 智慧引用(Smart Reference)代理:當一個物件被引用時,提供一些額外的操作,如將此物件被呼叫的次數記錄下來等。

下面介紹一下靜態代理和動態代理

代理模式分為靜態代理和動態代理 • 靜態代理:靜態代理就是編譯階段就生成代理類來完成對代理物件的一系列操作。
• 動態代理:動態代理是指在執行時動態生成代理類。即,代理類的位元組碼將在執行時生成並載入當前代理的 ClassLoader。

第二章 靜態代理

靜態代理:靜態代理就是編譯階段就生成代理類來完成對代理物件的一系列操作。
主題介面:

public   interface Subject  {    
    abstract   public   void  request(); 
}   

目標物件:

public   class  RealSubject  implements Subject  {                     
   public   void  request()  { 
       System.out.println( " From real subject. " );     
   }  
}  

代理物件:

public   class  StaticProxySubject  implements Subject  { 
    private  RealSubject  realSubject;  // 以真實角色作為代理角色的屬性  
    public  ProxySubject()  { }  
    public  void  request()  {  // 該方法封裝了真實物件的request方法        
    //懶載入,用的時候才載入
    if ( realSubject  ==   null  )  { 
        realSubject  =   new  RealSubject();        
    }   
    realSubject.request();  // 此處執行真實物件的request方法   
   } 
}

編寫客戶端類:

public class Client{
    StaticProxySubject sps = new StaticProxySubject();
    sps.request();
}

第三章 動態代理

動態代理:動態代理是指在執行時動態生成代理類。即,代理類的位元組碼將在執行時生成並載入當前代理的 ClassLoader。
生成動態代理的方法有很多: JDK中自帶動態代理,CGlib, javassist等。

3.1 JDK動態代理

Proxy類。該類即為動態代理類,該類最常用的方法為:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

newProxyInstance()方法用於根據傳入的介面型別interfaces返回一個動態建立的代理類的例項,方法中第一個引數loader表示代理類的類載入器,第二個引數interfaces表示被代理類實現的介面列表,第三個引數h表示所指派的呼叫處理程式類。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler {
    private Class<?> target;//委託類
    public MyInvocationHandler(Class<?> target){
        this.target=target;
    }
    //實際執行類bind
    public  Object bind(Class<?> target){
        //利用JDK提供的Proxy實現動態代理
        return  Proxy.newProxyInstance(target.getClassLoader(),
                new Class[]{target},this);
    }
    
    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        /**代理環繞**/
        //執行實際的方法
        Object invoke = method.invoke(target, args);
        return invoke;
    }
}

3.2 CGLIB動態代理

CGLIB動態代理實現相關類需要在專案中匯入 cglib-nodep-2.1_3.jar ,主要涉及兩個類:
MethodInterceptor介面。它是代理例項的呼叫處理程式實現的介面,該介面中定義瞭如下方法:public Object intercept(Object proxy, Method method, Object[] arg2, MethodProxy mp);

intercept()方法中第一個引數proxy表示代理類,第二個引數method表示需要代理的方法,第三個引數args表示代理方法的引數陣列,第四個引數mp用 來去呼叫被代理物件方法

package com.demo;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class MyInterceptor implements MethodInterceptor{    
    private Object target; ;//代理的目標物件
    public MyInterceptor(Object target) {
        this.target = target;
    } 
//proxy 在其上呼叫方法的代理例項    method攔截的方法    args  攔截的引數
 //invocation 用來去呼叫被代理物件方法
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, 
                                         MethodProxy invocation) throws Throwable {
        //1.記錄日誌 2.時間統計開始   3.安全檢查
        Object retVal = invocation.invoke(target, args);  
        //4.時間統計結束
        return retVal;   
    }
//建立代理物件的方法
    public Object proxy(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();//該類用於生成代理類      
        enhancer.setSuperclass(this.target.getClass());//設定父類
        enhancer.setCallback(this);//設定回撥用物件為本身
        return enhancer.create();

   }
}

相關文章