代理

《轉》JAVA動態代理(JDK和CGLIB)

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

該文章轉自:

http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

JAVA的動態代理

代理模式

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

按照代理的創建時期,代理類可以分為兩種。

靜態代理

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

動態代理

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

首先看一下靜態代理:

1、Count.java

/**

* 定義一個賬戶接口

*

* @author Administrator

*

*/publicinterfaceCount{// 查看賬戶方法publicvoidqueryCount();// 修改賬戶方法publicvoidupdateCount();? }

2、CountImpl.java

/**? * 委托類(包含業務邏輯)? *? *@authorAdministrator? *? */publicclassCountImplimplementsCount{@OverridepublicvoidqueryCount(){? ? ? ? ? System.out.println("查看賬戶方法...");? ? ? }@OverridepublicvoidupdateCount(){? ? ? ? ? System.out.println("修改賬戶方法...");? ? ? }? }? 、CountProxy.javapackagenet.battier.dao.impl;importnet.battier.dao.Count;/**? * 這是一個代理類(增強CountImpl實現類)? *? *@authorAdministrator? *? */publicclassCountProxyimplementsCount{privateCountImpl countImpl;/**? ? ? * 覆蓋默認構造器? ? ? *? ? ? *@paramcountImpl? ? ? */publicCountProxy(CountImpl countImpl){this.countImpl = countImpl;? ? ? }@OverridepublicvoidqueryCount(){? ? ? ? ? System.out.println("事務處理之前");// 調用委托類的方法;countImpl.queryCount();? ? ? ? ? System.out.println("事務處理之后");? ? ? }@OverridepublicvoidupdateCount(){? ? ? ? ? System.out.println("事務處理之前");// 調用委托類的方法;countImpl.updateCount();? ? ? ? ? System.out.println("事務處理之后");? ? ? }? }

3、TestCount.java

packagenet.battier.test;importnet.battier.dao.impl.CountImpl;importnet.battier.dao.impl.CountProxy;/**? *測試Count類? *? *@authorAdministrator? *? */publicclassTestCount{publicstaticvoidmain(String[] args){? ? ? ? ? CountImpl countImpl =newCountImpl();? ? ? ? ? CountProxy countProxy =newCountProxy(countImpl);? ? ? ? ? countProxy.updateCount();? ? ? ? ? countProxy.queryCount();? ? ? }? }

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

再來看一下動態代理:

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

packagenet.battier.dao;publicinterfaceBookFacade{publicvoidaddBook();? }

2、BookFacadeImpl.java

packagenet.battier.dao.impl;importnet.battier.dao.BookFacade;publicclassBookFacadeImplimplementsBookFacade{@OverridepublicvoidaddBook(){? ? ? ? ? System.out.println("增加圖書方法。。。");? ? ? }? }

、BookFacadeProxy.java

packagenet.battier.proxy;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;/**? * JDK動態代理代理類? *? *@authorstudent? *? */publicclassBookFacadeProxyimplementsInvocationHandler{privateObject target;/**? ? ? * 綁定委托對象并返回一個代理類? ? ? *@paramtarget? ? ? *@return*/publicObjectbind(Object target){this.target = target;//取得代理對象returnProxy.newProxyInstance(target.getClass().getClassLoader(),? ? ? ? ? ? ? ? ? target.getClass().getInterfaces(),this);//要綁定接口(這是一個缺陷,cglib彌補了這一缺陷)}@Override/**

* 調用方法

*/publicObjectinvoke(Object proxy, Method method, Object[] args)throwsThrowable{? ? ? ? ? Object result=null;? ? ? ? ? System.out.println("事物開始");//執行方法result=method.invoke(target, args);? ? ? ? ? System.out.println("事物結束");returnresult;? ? ? }? }

3、TestProxy.java

packagenet.battier.test;importnet.battier.dao.BookFacade;importnet.battier.dao.impl.BookFacadeImpl;importnet.battier.proxy.BookFacadeProxy;publicclassTestProxy{publicstaticvoidmain(String[] args){? ? ? ? ? BookFacadeProxy proxy =newBookFacadeProxy();? ? ? ? ? BookFacade bookProxy = (BookFacade) proxy.bind(newBookFacadeImpl());? ? ? ? ? bookProxy.addBook();? ? ? }? }

但是,JDK的動態代理依靠接口實現,如果有些類并沒有實現接口,則不能使用JDK代理,這就要使用cglib動態代理了。

Cglib動態代理

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

示例

1、BookFacadeCglib.java

publicinterfaceBookFacade{publicvoidaddBook();? }

2、BookCadeImpl1.java

package net.battier.dao.impl;/**

* 這個是沒有實現接口的實現類

*

* @author student

*

*/publicclassBookFacadeImpl1{publicvoidaddBook(){? ? ? ? ? System.out.println("增加圖書的普通方法...");? ? ? }? }

3、BookFacadeProxy.java

packagenet.battier.proxy;importjava.lang.reflect.Method;importnet.sf.cglib.proxy.Enhancer;importnet.sf.cglib.proxy.MethodInterceptor;importnet.sf.cglib.proxy.MethodProxy;/**? * 使用cglib動態代理? *? *@authorstudent? *? */publicclassBookFacadeCglibimplementsMethodInterceptor{privateObject target;/**? ? ? * 創建代理對象? ? ? *? ? ? *@paramtarget? ? ? *@return*/publicObjectgetInstance(Object target){this.target = target;? ? ? ? ? Enhancer enhancer =newEnhancer();? ? ? ? ? enhancer.setSuperclass(this.target.getClass());// 回調方法enhancer.setCallback(this);// 創建代理對象returnenhancer.create();? ? ? }@Override// 回調方法publicObjectintercept(Object obj, Method method, Object[] args,

MethodProxy proxy)throwsThrowable{? ? ? ? ? System.out.println("事物開始");? ? ? ? ? proxy.invokeSuper(obj, args);? ? ? ? ? System.out.println("事物結束");returnnull;? ? ? }? }

4、TestCglib.java

packagenet.battier.test;importnet.battier.dao.impl.BookFacadeImpl1;importnet.battier.proxy.BookFacadeCglib;publicclassTestCglib{publicstaticvoidmain(String[] args){? ? ? ? ? BookFacadeCglib cglib=newBookFacadeCglib();? ? ? ? ? BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(newBookFacadeImpl1());? ? ? ? ? bookCglib.addBook();? ? ? }? }

以上為轉載內容,下面是自己寫的Test

packagecom.xt.test;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;interfaceAnim{publicvoidsound();publicvoidname();}classDogimplementsAnim{@Overridepublicvoidsound(){// TODO Auto-generated method stubSystem.out.println("汪汪汪!");? ? }@Overridepublicvoidname(){// TODO Auto-generated method stubSystem.out.println("一條小狗!");? ? }}classCatimplementsAnim{@Overridepublicvoidsound(){// TODO Auto-generated method stubSystem.out.println("喵喵喵!");? ? }@Overridepublicvoidname(){// TODO Auto-generated method stubSystem.out.println("一只小貓!");? ? }}interfaceCar{publicvoidname();publicvoidcolor();publicvoidrun();}classBmwimplementsCar{@Overridepublicvoidname(){// TODO Auto-generated method stubSystem.out.println("一輛寶馬!");? ? }@Overridepublicvoidcolor(){// TODO Auto-generated method stubSystem.out.println("黑色!");? ? }@Overridepublicvoidrun(){// TODO Auto-generated method stubSystem.out.println("車子開動!");? ? }}classWmimplementsCar{@Overridepublicvoidname(){// TODO Auto-generated method stubSystem.out.println("一輛大眾!");? ? }@Overridepublicvoidcolor(){// TODO Auto-generated method stubSystem.out.println("銀色!");? ? }@Overridepublicvoidrun(){// TODO Auto-generated method stubSystem.out.println("車子開動!");? ? }}/** * JDK動態代理代理類 *? *@authorstudent *? */classDynamicProxyimplementsInvocationHandler{privateObject target;/**? ? * 綁定委托對象并返回一個代理類? ? *? ? ? *@paramtarget? ? *@return*/publicObjectbind(Object target){this.target = target;// 取得代理對象Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(),? ? ? ? ? ? ? ? target.getClass().getInterfaces(),this);// 要綁定接口(這是一個缺陷,cglib彌補了這一缺陷)returno;? ? }@Override/**

* 調用方法

*/publicObjectinvoke(Object proxy, Method method, Object[] args)throwsThrowable{? ? ? ? Object result =null;if(targetinstanceofAnim && method.getName().equals("sound"))? ? ? ? ? ? System.out.println("準備叫:");if(targetinstanceofCar && method.getName().equals("run"))? ? ? ? ? ? System.out.println("車子點火!");// 執行方法result = method.invoke(target, args);if(targetinstanceofAnim && method.getName().equals("sound"))? ? ? ? ? ? System.out.println("叫響了");if(targetinstanceofCar && method.getName().equals("run"))? ? ? ? ? ? System.out.println("車子停了!");returnresult;? ? }}publicclassDynamicProxyTest{publicstaticvoidmain(String[] args){? ? ? ? DynamicProxy proxy =newDynamicProxy();? ? ? ? Anim dog = (Anim) proxy.bind(newDog());? ? ? ? dog.name();? ? ? ? dog.sound();? ? ? ? System.out.println("---------------------");? ? ? ? Anim cat = (Anim) proxy.bind(newCat());? ? ? ? cat.name();? ? ? ? cat.sound();? ? ? ? System.out.println("---------動態的精髓來了,無需再構建代理,不同接口使用相同代理即可------------");// 動態的精髓來了,無需再構建代理,不同接口使用相同代理即可Car bmw = (Car) proxy.bind(newBmw());? ? ? ? bmw.name();? ? ? ? bmw.color();? ? ? ? bmw.run();? ? ? ? System.out.println("---------------------");? ? ? ? Car wm = (Car) proxy.bind(newWm());? ? ? ? wm.name();? ? ? ? wm.color();? ? }}輸出結果:一條小狗!準備叫:汪汪汪!叫響了---------------------一只小貓!準備叫:喵喵喵!叫響了---------動態的精髓來了,無需再構建代理,不同接口使用相同代理即可------------一輛寶馬!黑色!車子點火!車子開動!車子停了!---------------------一輛大眾!銀色!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容