設計模式之代理模式(全面講解)

      設計模式是一套被反覆使用,多數人知曉,通過分類編目的,代碼設計的總結,也能夠說是前人的智慧結晶。學習設計模式能讓咱們對一些應用場景使用相同的套路達到很好的效果,我會不定時更新一些本身對設計模式的理解的文章,從定義,實現,應用場景來講說設計模式,今天我要說的對象是代理模式java

  一:定義   設計模式

    代理模式:給某一個對象提供一個代理或佔位符,並由代理對象來控制對原對象的訪問。代理模式是經常使用的結構型設計模式之一,當沒法直接訪問某個對象或不適合直接訪問某個對象時能夠經過一個代理對象來間接訪問,代理(Proxy)是一種設計模式,提供了對目標對象另外的訪問方式;即經過代理對象訪問目標對象.這樣作的好處是:能夠在目標對象實現的基礎上,加強額外的功能操做,即擴展目標對象的功能。這麼作也服務開閉原則(對擴展開放對修改關閉)的要求。代理模式的關鍵點是:代理對象會調用被代理對象,代理對象對被代理對象的功能作了擴展。框架

    打個比方,咱們想要購買國外的某些商品,因爲各類成本問題,直接去一趟國外不划算,因而咱們找了代購幫咱們購買,這裏的代購幫咱們到國外的實體店完成了購買的動做,他就是實體店的代理,而實體店是被代理,而咱們和代購直接的溝通交涉,至關因而對被代理功能的擴展,而這一步是在咱們和代理直接完成,不會影響被代理的商店ide

 

  二:實現函數

    從實現上來講,代理模式能夠分爲靜態,動態代理和子類代理工具

    靜態代理:靜態代理在使用時,須要定義接口或者父類,被代理對象與代理對象一塊兒實現相同的接口或者是繼承相同父類,所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委託類的關係在運行前就肯定了。下面展現代碼實現性能

    //定義接口學習

    interface UserDao{測試

        void save();
    }
    //被代理類實現接口
    class UserService implements UserDao{
     @Override
     public void save() {
      System.out.println("這是須要被代理的保存方法");
     }
    }

    //代理類代理相關的方法
    class UserDaoProxy implements UserDao{
      //在代理類中引用被代理類,這裏只是使用最簡單的方法
     private UserDao iUserDao;
    public UserDaoProxy(UserDao iUserDao){
    this.iUserDao=iUserDao;
    }
      //引用並擴展被代理類的方法
    @Override
    public void save() {
     System.out.println("這裏編寫須要擴展的內容");
    iUserDao.save();
    System.out.println("保存成功或者失敗以後的操做");
  }

  //測試一下
public static void main(String[] df){

UserDaoProxy proxy=new UserDaoProxy(new UserService()); proxy.save();
}
}  
    動態代理:代理對象是動態生成的,不須要實現父類接口
      動態的生成一個對象,很天然的就想到反射。JDK中有動態生成代理的包(java.lang.reflect.Proxy),是專門爲建立動態代理對象存在的,它的內部實現是使用了反射技術,JDK實現代理只須要使用newProxyInstance方法,可是該方法須要接收三個參數,完整的寫法是: static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )注意該方法是在Proxy類中是靜態方法,且接收的三個參數依次爲:        ● ClassLoader loader,:指定當前目標對象使用類加載器,獲取加載器的方法是固定的       ● Class<?>[] interfaces,:目標對象實現的接口的類型,使用泛型方式確認類型       ● InvocationHandler h:事件處理,執行目標對象的方法時,會觸發事件處理器的方法,會把當前執行目標對象的方法做爲參數傳入。
    這裏使用一個工廠類建立動態代理對象來代替上面的代理對象,其餘方法不變
    class ProxyFactory{      public static Object getProxyInstance(Object target){       return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {       @Override        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {          System.out.println("這是代理對象擴展的內容");         Object object=method.invoke(target,args);        return object;       }        });     }
      //測試一下
    public static void  main(String[] df){

UserDao object=(UserDao)ProxyFactory.getProxyInstance(new UserService());
object.save();
}
    }
      子類代理:
          上面的靜態代理和動態代理模式都是要求目標對象是實現一個接口的目標對象,可是有時候目標對象只是一個單獨的對象,並無實現任何的接口,這個時候就可使用以目標對象子類的方式類實現代理,這種方法就叫作:Cglib代理

        Cglib代理,也叫做子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.

        ● JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,若是想代理沒有實現接口的類,就可使用Cglib實現.
        ● Cglib是一個強大的高性能的代碼生成包,它能夠在運行期擴展java類與實現java接口.它普遍的被許多AOP的框架使用,例如Spring AOP和synaop,爲他們提供方法的interception(攔截)
        ● Cglib包的底層是經過使用一個小而塊的字節碼處理框架ASM來轉換字節碼並生成新的類.不鼓勵直接使用ASM,由於它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉.
          Cglib子類代理實現方法:
        1.須要引入cglib的jar文件,可是Spring的核心包中已經包括了Cglib功能,因此直接引入pring-core-3.2.5.jar便可.
        2.引入功能包後,就能夠在內存中動態構建子類
        3.代理的類不能爲final,不然報錯
        4.目標對象的方法若是爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.this

         //被代理對象,沒有實現任何接口
      class UserDao {

              public void save() {
                System.out.println("----已經保存數據!----");
              }
      }

      class ProxyFactory implements MethodInterceptor{

          //給目標對象建立一個代理對象
          public Object getProxyInstance(Object target){
            //1.工具類
            Enhancer en = new Enhancer();
            //2.設置父類
            en.setSuperclass(target.getClass());
            //3.設置回調函數
            en.setCallback(this);
            //4.建立子類(代理對象)
            return en.create();
          }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
              System.out.println("這是須要擴展的內容...");

            //執行目標對象的方法
            Object returnValue = method.invoke(target, args);

              System.out.println("這是須要擴展的內容...");

            return returnValue;
        }

        //測試一下

        public static void main(String[] arg){  

          //被代理對象
          UserDao target = new UserDao();

          //代理對象
          UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();

          //執行代理對象的方法
          proxy.save();

        }
      }

 

  三:應用場景

    從應用場景來講,代理模式能夠分爲遠程代理,緩衝代理,智能引用代理,虛擬代理,保護代理等等

    (1) 當客戶端對象須要訪問遠程主機中的對象時可使用遠程代
    (2) 當須要用一個消耗資源較少的對象來表明一個消耗資源較多的對象,從而下降系縮短運行時間時可使用虛擬代理,例如一個對象須要很長時間才能完成加
    (3) 當須要爲某一個被頻繁訪問的操做結果提供一個臨時存儲空間,以供多個客戶端共享訪問這些結果時可使用緩衝代理。經過使用緩衝代理,系統無須在客戶端每一次訪問時都從新執行操做,只需直接從臨時緩衝區獲取操做結果便可。
    (4) 當須要控制對一個對象的訪問,爲不一樣用戶提供不一樣級別的訪問權限時可使用保護代理。
    (5) 當須要爲一個對象的訪問(引用)提供一些額外的操做時可使用智能引用代理。

相關文章
相關標籤/搜索