Java動態代理和cglib動態代理

來源:互聯網
上載者:User

標籤:voc   部分   odi   編寫   tin   framework   after   iar   ati   

一、何為動態代理?

    建議看動態代理前,先看看反射 點擊這裡

  先看一個小案例,假設有咖啡類A,B,C。有添加物類a,b,c,。現在對咖啡類進行增強。( 比如像向啡中加糖,牛奶等這個意思)。

  對一個類進行增強可以有三種方式:

  1.繼承,可以直接繼承父類的屬性和方法,在新增自己的屬性和方法。那麼我們對每種咖啡進行增強,一共需要寫3*3個繼承類來完成。增強對象和內容都不可變。

  2.裝飾者模式,在需要增強類中組合一個需要增強屬性。那麼我們需要寫三個組合咖啡類,每個類都添加一個添加物介面屬性,向添加物介面傳不同的實現就可以對該咖啡類進行不同的增強。增強內容可變,但增強對象是不可變的。

  3.動態代理,通過一個代理工廠,向其傳入需要增強咖啡類和添加物類,其自動產生一個增強後的代理類。我們只需要實現代理工廠這個類即可。

二、java動態代理的實現

  java中有一個類Proxy,其有一個靜態方法Object proxy = Proxy.newProxyInstance(ClassLoader classLoader , Class[] interfaces , InvocationHandler); 通過此方法就可以得到一個代理對象。下面說一下這三個參數。

  ClassLoader 類載入器,負責載入代理的類載入器。關於類載入器詳細解答 點擊這裡

  Class[] 目標類所實現的介面們,用Class對象數組表示。

  Invocationhandler 處理器,當調用代理類proxy的方法時,實際上是調用處理器的 invoke()方法。下面在對invoke方法進行詳解。

  Object invoke(Object proxy , Method method , Object[] args ) ;

  proxy:代理對象。  method: 執行的代理對象的方法。 args: 調用代理對象的方法時傳入的參數。

  下面貼代碼實現

目標類介面

package cn.edu;public interface Waiter {    public void server();}

 

前置通知介面

package cn.edu;public interface AdviceBefore {    public void before();}

後置通知介面

package cn.edu;public interface AdviceAfter {    public void after();}

代理工廠

package cn.edu;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 代理工廠,用於生產代理對象 *  * @author WangHang * */public class ProxyFactory {    private Object target;               // 目標對象    private AdviceBefore adviceBefore;   // 前置通知    private AdviceAfter adviceAfter;     // 後置通知    // 組建代理程式對象核心方法    public Object getProxyInstance() {        ClassLoader loader = ProxyFactory.class.getClassLoader();    // 類載入器        Class[] interfaces = target.getClass().getInterfaces();      // 類所實現的介面        InvocationHandler h = new InvocationHandler() {              // 處理器            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                if (adviceBefore != null) {                    adviceBefore.before();                           // 調用前置通知                }                Object result = method.invoke(target, args);         // 調用目標方法                if (adviceAfter != null) {                           // 調用後置通知                    adviceAfter.after();                }                return result;            }        };        Object proxy = Proxy.newProxyInstance(loader, interfaces, h); // 通過給定的三個參數產生動態代理對象        return proxy;                                                 // 返回代理對象    }    public AdviceBefore getAdviceBefore() {        return adviceBefore;    }    public void setAdviceBefore(AdviceBefore adviceBefore) {        this.adviceBefore = adviceBefore;    }    public AdviceAfter getAdviceAfter() {        return adviceAfter;    }    public void setAdviceAfter(AdviceAfter adviceAfter) {        this.adviceAfter = adviceAfter;    }    public Object getTarget() {        return target;    }    public void setTarget(Object target) {        this.target = target;    }}

測試,介面的實現用的匿名內部類

package cn.edu;import org.junit.Test;public class ProxyTest {    @Test    public void fun() {        Waiter waiter = new Waiter() {                         //產生目標對象            @Override            public void server() {                System.out.println("服務中。。。");            }        };                AdviceBefore before = new AdviceBefore() {             //產生前置通知            @Override            public void before() {                System.out.println("歡迎光臨!");            }        };                AdviceAfter after = new AdviceAfter() {            @Override            public void after() {                System.out.println("謝謝光臨!");                //產生後置通知            }        };                ProxyFactory proxyFactory = new ProxyFactory();        //產生工廠                proxyFactory.setTarget(waiter);                          //設定目標對象        proxyFactory.setAdviceBefore(before);                    //設定前置通知        proxyFactory.setAdviceAfter(after);                      //設定後置通知                Waiter proxy = (Waiter)proxyFactory.getProxyInstance();  //獲得代理對象                proxy.server();                                          //調用目標對象的方法    }    }

運行結果:

三、cglib動態代理的實現

   java動態代理的實現要求目標類實現介面,如果沒有介面就無法完成動態代理。cglib(Code Genaration Liarbry)是一個強大的Code產生類庫 ,它可以在程式運行期間擴充Java類。它不要求目標類實現介面,它採用的是繼承的方式來擴充目標類。

  下面用cglib實現代理,其餘類不用變,只需更改代理工廠,並且給Waiter添加一個實作類別WaiterImpl,cglib代理是需要類的,用匿名類是會報錯的。更改的部分如下。

Waiter實作類別

package cn.edu.cglibProxy;public class WaiterImpl implements Waiter {    @Override    public void server() {        // TODO Auto-generated method stub        System.out.println("服務中。。。");    }}

代理工廠類

package cn.edu.cglibProxy;import java.lang.reflect.Method;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;/** * 代理工廠,用於生產代理對象 *  * @author WangHang * */public class ProxyFactory {    private Object target;             // 目標對象    private AdviceBefore adviceBefore; // 前置通知    private AdviceAfter adviceAfter;   // 後置通知    // 組建代理程式對象核心方法    public Object getProxyInstance() {        Enhancer enhancer = new Enhancer();                enhancer.setSuperclass(target.getClass());                enhancer.setCallback(new MethodInterceptor() {            @Override            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {                adviceBefore.before();                Object result = method.invoke(target, args);                adviceAfter.after();                return result;            }        });        Object proxy = enhancer.create();        return proxy;            // 返回代理對象    }    public AdviceBefore getAdviceBefore() {        return adviceBefore;    }    public void setAdviceBefore(AdviceBefore adviceBefore) {        this.adviceBefore = adviceBefore;    }    public AdviceAfter getAdviceAfter() {        return adviceAfter;    }    public void setAdviceAfter(AdviceAfter adviceAfter) {        this.adviceAfter = adviceAfter;    }    public Object getTarget() {        return target;    }    public void setTarget(Object target) {        this.target = target;    }}

測試類別

package cn.edu.cglibProxy;import org.junit.Test;public class ProxyTest {    @Test    public void fun() {                Waiter waiter = new WaiterImpl();                AdviceBefore before = new AdviceBefore() {             //產生前置通知            @Override            public void before() {                System.out.println("歡迎光臨!");            }        };                AdviceAfter after = new AdviceAfter() {            @Override            public void after() {                System.out.println("謝謝光臨!");                //產生後置通知            }        };                ProxyFactory proxyFactory = new ProxyFactory();        //產生工廠                proxyFactory.setTarget(waiter);                          //設定目標對象        proxyFactory.setAdviceBefore(before);                    //設定前置通知        proxyFactory.setAdviceAfter(after);                      //設定後置通知                Waiter proxy = (Waiter)proxyFactory.getProxyInstance();  //獲得代理對象                proxy.server();                                          //調用目標對象的方法    }    }

測試結果

 

四、總結  

  通過上面的兩個例子,可以體會到動態代理的優勢。靜態代理是由程式員手動編寫好原始碼,在程式運行時已經存在了class檔案,而動態代理則是在運行時根據需要動態產生的,不僅減少了工作量,使用時也更加靈活。

Java動態代理和cglib動態代理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.