Java 動態代理(JDK 和 cglib)[轉]

原文

代理模式

代理模式是常用的 Java 設計模式,它的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身并不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。

代理的分類

  1. 靜態代理

    由程序員創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。

  2. 動態代理

    在程序運行時,運用反射機制動態創建而成。

靜態代理

  1. Count.java

    package com.yangruihan.dao;
    
    /**
     * 定義一個賬戶接口
     */
    public interface Count {
        
        // 查看賬戶方法
        public void queryCount();
    
        // 修改賬戶方法
        public void updateCount();
    }
    
  2. CountImpl.java

    package com.yangruihan.dao.impl;
    
    import com.yangruihan.dao.Count;
    
    /**
     * 委托類(包含業務邏輯)
     */
    public class CountImpl implements Count {
        
        @Override
        public void queryCount() {
            System.out.println("查看用戶...");
        }
    
        @Override
        public void updateCount() {
            System.out.println("修改賬戶...");
        }
    }
    
  3. CountProxy.java

    package com.yangruihan.dao.impl;
    
    import com.yangruihan.dao.Count;
    
    /**
     * 這是一個代理類(增強 CountImpl 實現類)
     */
    public class CountProxy implements Count {
        private CountImpl countImpl;
    
        /**
         * 覆蓋默認構造器
         */
        public CountProxy(CountImpl countImpl) {
            this.CountImpl = countImpl;
        }
    
        @Override
        public void queryCount() {
            System.out.println("事務處理之前...");
            // 調用委托類的方法
            countImpl.queryCount();
            System.out.println("事務處理之后...");
        }
    
        @Override
        public void updateCount() {
            System.out.println("事務處理之前...");
            // 調用委托類的方法
            countImpl.updateCount();
            System.out.println("事務處理之后...");   
        }
    }
    
  4. TestCount.java

    package com.yangruihan.test;
    
    import com.yangruihan.dao.impl.CountImpl;
    import com.yangruihan.dao.impl.CountProxy;
    
    /**
     * 測試類
     */
    public class TestCount {
        public static void main(String[] args) {
            CountImpl countImpl = new CountImpl();
            CountProxy countProxy = new CountProxy(countImpl);
            countProxy.updateCount();
            countProxy.queryCount();
        }
    }
    

動態代理

觀察上面代碼可以發現每一個代理類只能為一個接口服務,這樣一來程序開發中必然會產生過多的代理類,而且,所有的代理操作除了調用的方法不一樣之外,其他的操作都一樣,則此時肯定是有重復代碼的。解決這一問題最好的做法是可以通過一個代理類完成全部的代理功能,那么此時就必須使用動態代理完成。

  1. JDK 動態代理(包含一個類和一個接口):

    • InvocationHandler接口:

      public interface InvocationHandler {
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
      }
      

      其中參數說明:

      • Object proxy:指被代理的對象
      • Method method:要調用的方法
      • Object[] args:方法調用時所需要的參數

      可以將InvocationHandler接口的子類想象成一個代理的最終操作類,替換掉ProxySubject

    • Proxy類:

      Proxy類是專門完成代理的操作類,可以通過此類為一個或多個接口動態地生成實現類,此類提供如下的操作方法:

      public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
      

      其中參數說明:

      • ClassLoader loader:類加載器
      • Class<?>[] interfaces:得到全部的接口
      • InvocationHandler h:得到InvocationHandler接口的子類實例

      Ps:類加載器:

        *在`Proxy`類中的`newProxyInstance()`方法中需要一個`ClassLoader`類的實例,`ClassLoader`實際上對應的是類加載器,在Java中主要有一下三種類加載器:*
      
        - *Booststrap ClassLoader:此加載器采用 C++ 編寫,一般開發中是看不到的*
        - *Extendsion ClassLoader:用來進行擴展類的加載,一般對應的是`jre\lib\ext`目錄中的類*
        - *AppClassLoader:(默認)加載`classpath`指定的類,是最常用的一種加載器*
      

    與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由 Java 反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因為 Java 反射機制可以生成任意類型的動態代理類。java.lang.reflect包中的Proxy類和InvocationHandler接口提供了生成動態代理類的能力。

    程序演示:

    1. BookFacade.java

      package com.yangruihan.dao;
      
      public interface BookFacade {
          public void addBook();
      }
      
    2. BookFacadeImpl.java

      package com.yangruihan.dao.impl;
      
      import com.yangruihan.dao.BookFacade;
      
      public class BookFacadeImpl implements BookFacade {
      
          @Override
          public void addBook() {
              System.out.println("增加圖書方法...");
          }
      }
      
    3. BookFacadeProxy.java

      package com.yangruihan.proxy;
      
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      /**
       * JDK 動態代理代理類
       */
      public class BookFacadeProxy implements InvocationHandler {
          private Object target;
      
          /**
           * 綁定委托對象并返回一個代理類
           */
          public Object bind(Object target) {
              this.target = target;
      
              // 取得代理對象
              return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); // 該方法需要綁定接口(這是一個缺陷,cglib 彌補了這一缺陷)
          }
      
          /**
           * 調用方法
           */
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              Object result = null;
              System.out.println("事務開始...");
              // 執行方法
              result = method.invoke(target, args);
              System.out.println("事務結束...");
              return result;
          }
      }
      
    4. TestProxy.java

      package com.yangruihan.test;
      
      import com.yangruihan.dao.BookFacade;
      import com.yangruihan.dao.impl.BookFacadeImpl;
      import com.yangruihan.proxy.BookFacadeProxy;
      
      public class TestProxy {
      
          public static void main(String[] args) {
              BookFacadeProxy proxy = new BookFacadeProxy();
              BookFacade bookProxy = (BookFacade)proxy.bind(new BookFacadeImpl());
              bookProxy.addBook();
          }
      }
      
  2. cglib 代理

    JDK 的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現 JDK 動態代理,cglib 是針對類來實現代理的,它的原理是對指定的目標類生成一個子類,并覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。

    程序演示:

    1. BookFacade.java

      package com.yangruihan.dao;
      
      public interface BookFacade {
          public void addBook();
      }
      
    2. BookFacadeImpl.java

      package com.yangruihan.dao.impl;
      
      /**
       * 這是沒有實現接口的實現類
       */
      public class BookFacadeImpl {
          public void addBook() {
              System.out.println("增加圖書的普通方法...");
          }
      }
      
    3. BookFacadeProxy.java

      package com.yangruihan.proxy;
      
      import java.lang.reflect.Method;
      
      import net.sf.cglib.proxy.Enhancer;  
      import net.sf.cglib.proxy.MethodInterceptor;  
      import net.sf.cglib.proxy.MethodProxy;  
      
      /**
       * 使用 cglib 動態代理
       */
      public class BookFacadeCglib implements MethodInterceptor {
      
          private Object target;
      
          /**
           * 創建代理對象
           */
          public Object getInstance(Object target) {
              this.target = target;
              Enhancer enhancer = new Enhancer();
              enhancer.setSuperClass(this.target.getClass());
      
              // 回調方法
              enhancer.setCallBack(this);
      
              // 創建代理對象
              return enhancer.create();
          }
      
          /**
           * 回調方法
           */
          @Override
          public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
              System.out.println("事務開始...");
              proxy.invokeSuper(obj, args);
              System.out.println("事務結束...");
      
              return null;
          }
      }
      
    4. TestCglib.java

      package com.yangruihan.test;
      
      import com.yangruihan.dao.BookFacadeImpl;
      import com.yangruihan.proxy.BookFacadeCglib;
      
      public class TestCglib {
      
          public static void main(String[] args) {
              BookFacadeCglib cglib = new BookFacadeCglib();
              BookFacadeImpl bookCglib = (BookFacadeImpl)cglib.getInstance(new BookFacadeImpl());
              
              bookCglib.addBook();
          }
      }
      
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • java動態代理(JDK和cglib) JAVA的動態代理 代理模式 代理模式是常用的java設計模式,他的特征是...
    Andy_1777閱讀 466評論 0 0
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • 0.前言 本文主要想闡述的問題如下:什么動態代理(AOP)以及如何用JDK的Proxy和InvocationHan...
    SYFHEHE閱讀 2,308評論 1 7
  • 《轉》JAVA動態代理(JDK和CGLIB) 代理模式是常用的java設計模式,他的特征是代理類與委托類有同樣的接...
    奈何心善閱讀 266評論 0 0
  • 靜靜,這是一封給你的信:今天又下雨了,17年的春天似乎是在雨中度過的。同樣是這樣靜謐的夜晚,兩個月前,我在喂貓;現...
    TECHNIUM閱讀 259評論 0 0