動态代理
java中動态代理機制的引入使得代理模式的思想更加完善與進步,它允許動态的建立代理并支援對動态的對所代理的方法進行調用。java動态代理類位于java.lang.reflect包下,一般主要涉及到以下兩個類:
(1). interface invocationhandler:該接口中僅定義了一個方法object:invoke(object obj,method method, object[] args)。在實際使用時,第一個參數obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的參數數組。這個抽象方法在代理類中動态實作。
(2).proxy:該類即為動态代理類,作用類似于上例中的proxysubject,其中主要包含以下内容:
protected proxy(invocationhandler h):構造函數,估計用于給内部的h指派。
static class getproxyclass (classloader loader, class[] interfaces):獲得一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的全部接口的數組。
static object newproxyinstance(classloader loader, class[] interfaces, invocationhandler h):傳回代理類的一個執行個體,傳回後的代理類可以當作被代理類使用(可使用被代理類的在subject接口中聲明過的方法)。
所謂dynamic proxy是這樣一種class:它是在運作時生成的class,在生成它時你必須提供一組interface給它,然後該class就宣稱它實作了這些interface。你當然可以把該class的執行個體當作這些interface中的任何一個來用。當然啦,這個dynamic proxy其實就是一個proxy,它不會替你作實質性的工作,在生成它的執行個體時你必須提供一個handler,由它接管實際的工作。 下面我們通過動态代理來重新實作上面發送資訊的例子!
在上面的例子基礎上,我們先添加一個通過短信來發送消息的處理類:
public class smsmessage implements messagehandler {
@override
public void sendmessage(string msg) {
// todo auto-generated method stub
system.out.println("sms message :" + msg+" sent !");
}
//動态代理類
import java.lang.reflect.invocationhandler;
import java.lang.reflect.method;
public class dynamicmessageproxy implements invocationhandler {
private static int count;
private messagehandler msghandler;
public dynamicmessageproxy(messagehandler handler) {
msghandler = handler;
public object invoke(object proxy, method method, object[] args)
throws throwable {
system.out.println("++++++++=============+++++++++");
system.out.println("proxy:" + proxy.getclass());
system.out.println("method:" + method);
if (args != null && args.length == 1 && checkmessage((string) args[0])) {
count++;
system.out.println("message sent:" + count);
return method.invoke(msghandler, args);
return null;
private boolean checkmessage(string msg) {
return msg != null && msg.length() > 10;
//下面是調用
import java.lang.reflect.proxy;
public class mainclass {
private static void runproxy(messagehandler handler) {
handler.sendmessage("message for test");
/**
* @param args
*/
public static void main(string[] args) {
// runproxy(new emailmessage());
// system.out.println("++++++++++++++++proxy++++++++++++++++++");
// runproxy(new messageproxy());
messagehandler handler = new emailmessage();
runproxy(handler);
messagehandler proxy = (messagehandler) proxy.newproxyinstance(
messagehandler.class.getclassloader(),
new class[] { messagehandler.class }, new dynamicmessageproxy(
handler));
runproxy(proxy);
system.out.println("++++++++++++++++++++++++++++++++++");
// 短信方式
handler = new smsmessage();
proxy = (messagehandler) proxy.newproxyinstance(messagehandler.class
.getclassloader(), new class[] { messagehandler.class },
new dynamicmessageproxy(handler));
下面為以上方法的輸出:
message for test send!!
++++++++=============+++++++++
proxy:class $proxy0
method:public abstract void messagehandler.sendmessage(java.lang.string)
message sent:1
++++++++++++++++++++++++++++++++++
sms message :message for test sent !
message sent:2
以上例子中,通過調用proxy.newproxyinstance方法建立動态代理對象,該方法需要傳入一個 類加載器、一組希望代理實作的接口清單、invocationhandler 接口的一個具體實作。動态代理可以将所有調用重定向到調用處理器,通常我們會向該處理器傳遞一個時間對象的引用。invoke()方法中傳遞進來了代理對象,當你需要區分請求來源時這是非常有用的,例如你可以通過判斷傳入的方法名屏蔽掉某些方法的執行!動态代理機制并不是會很頻繁使用的方法,它通常用來解決一些特定情況下的問題,是以不要盲目的為了使用而使用,要根據自己的實際需求來決定!
動态代理的應用十分廣泛,在struts2的攔截器中就用到了動态代理機制。還是以買車票為例子,現在用動态代理來實作,其中不懂的接口方法查查java api就會明白了,在此不多做解釋,代碼如下(注意passengerproxy類):
import java.lang.reflect.*;
public interface passenger {
public void buyticket();
public class realpassenger implements passenger {
public void buyticket() {
// todo auto-generated method stub
system.out.println("購買了車票");
// 用動态代理實作
/<code>/invocationhandler</code> 是代理執行個體的調用處理程式 實作的接口。 每個代理執行個體都具有一個關聯的調用處理程式。對代理執行個體調用方法時,将對方法調用進行編碼并将其指派到//它的調用處理程式的 <code>invoke</code> 方法。
public class passengerproxy implements invocationhandler {
public object obj;
// 把obj交給代理類
public object obj(object obj) {
this.obj = obj;
return proxy.newproxyinstance(obj.getclass().getclassloader(), obj
.getclass().getinterfaces(), this);
throws throwable {
system.out.println("通過代理");
method.invoke(obj, args);
return null;
public class client {
passengerproxy proxy = new passengerproxy();
passenger passenger = (passenger) proxy.obj(new realpassenger());
passenger.buyticket();
再來一個更牛的例子
/**
* 相親接口
*
* @author zhengt
* @time jun 3, 2095 3:13:03 pm
*/
public interface xiangqininterface {
/**
* 相親方法
*/
public void xiangqin();
}
* 張三相親實作類
* @time jun 3, 2095 3:14:48 pm
public class zhangsanxiangqininterfaceimpl implements xiangqininterface {
public void xiangqin() {
system.out.println("張三去相親,娶個漂亮老婆。");
}
import java.lang.reflect.invocationhandler;
import java.lang.reflect.method;
* 相親可是一輩子的大事,相親前要準備一下,打扮得帥氣些。
* @time jun 3, 2095 3:15:48 pm
public class readyinvocationhandler implements invocationhandler {
//相親接口的實作類,也就是張三相親類
private object zhangsan = null;
public readyinvocationhandler(object realsubject) {
this.zhangsan = realsubject;
public object invoke(object proxy, method m, object[] args) {
object result = null;
try {
/**
* 動态代理類$proxy0調用xiangqin方法時會調用它自己的xiangqin方法,
* 而它自己的xiangqin方法裡面調用的是super.h.invoke(this, , ),也就是父類proxy的h的invoke方法,
* 也就是readyinvocationhandler類的invoke方法。
* 是以,invoke(object proxy, method m, object[] args)種的proxy實際上就是動态代理類$proxy0,
* 如果你将其強轉成xiangqininterface然後調用它的xiangqin方法,然後它就會調用super.h.invoke(this, , ),這樣就會死循環。
*/
* 網上關于這裡最多問題就是object proxy放在這裡用來做什麼呢?這個我也不知道,
* 不過至少我們知道它到底是個什麼東西,具體做什麼用嘛就不得而知了
system.out.println(proxy.getclass().getsimplename());
system.out.println("張三相親前,代理人給他打扮了打扮。");
result = m.invoke(zhangsan, args);
} catch (exception ex) {
system.exit(1);
}
return result;
import java.lang.reflect.proxy;
* 張三來到了婚介所(相親現場),開始相親。
* @time jun 3, 2095 3:17:16 pm
public class hunjiesuo {
public static void main(string args[]) {
//先将張三相親這個相親的實作類執行個體化,也就是得到xiangqininterface接口的一個執行個體對象
xiangqininterface zhangsan = new zhangsanxiangqininterfaceimpl();
/**
* 得到zhangsanxiangqininterfaceimpl這個類的一個代理類,同時為代理類綁定了一個處理類readyinvocationhandler。
* 聽着很繞口,其實就是每次調用zhangsanxiangqininterfaceimpl這個子類的xiangqin方法時,
* 不是zhangsan這個zhangsanxiangqininterfaceimpl類的執行個體去調用,
* 而是這個zhangsanxiangqininterfaceimpl的代理類readyinvocationhandler去調用它自己的invoke方法,
* 這個invoke方法裡呢可以調用zhangsan這個執行個體的xiangqin方法
*/
* 在java種怎樣實作動态代理呢
* 第一步,我們要有一個接口,還要有一個接口的實作類,而這個實作類呢就是我們要代理的對象,
* 所謂代理呢也就是在調用實作類的方法時,可以在方法執行前後做額外的工作,這個就是代理。
* 第二步,我們要自己寫一個在要代理類的方法執行時,能夠做額外工作的類,而這個類必須繼承invocationhandler接口,
* 為什麼要繼承它呢?因為代理類的執行個體在調用實作類的方法的時候,不會調真正的實作類的這個方法,
* 而是轉而調用這個類的invoke方法(繼承時必須實作的方法),在這個方法中你可以調用真正的實作類的這個方法。
* 第三步,在要用代理類的執行個體去調用實作類的方法的時候,寫出下面兩段代碼。
xiangqininterface proxy = (xiangqininterface) proxy.newproxyinstance(
zhangsan.getclass().getclassloader(),
zhangsan.getclass().getinterfaces(),
new readyinvocationhandler(zhangsan));
proxy.xiangqin();
* 這裡要解釋下中部那段長長的代碼的意思,以及具體做了哪些工作?
* 第一,根據zhangsan.getclass().getclassloader()這個要代理類的類加載器和
* zhangsan.getclass().getinterfaces()要代理類所實作的所有的接口
* 作為參數調用proxy.getproxyclass(classloader loader, class<?>... interfaces)
* 的方法傳回代理類的java.lang.class對象,也就是得到了java動态生成的代理類$proxy0的class對象。
* 同時,java還讓這個動态生成的$proxy0類實作了要代理類的實作的所有接口,并繼承了proxy接口。
* 第二,執行個體化這個動态生成的$proxy0類的一個執行個體,執行個體化代理類的構造函數為proxy(invocationhandler h),
* 也就是說要執行個體化這個動态生成的$proxy0類,必須給它一個invocationhandler參數,也就是我們自己實作的用來在代理類
* 方法執行前後做額外工作的類readyinvocationhandler。
* 這 段代碼 proxy.newproxyinstance(zhangsan.getclass().getclassloader(),zhangsan.getclass().getinterfaces(),new readyinvocationhandler(zhangsan))
* 得到的其實是一個類名叫$proxy0 extends proxy implements xiangqininterface的類。
* 第三,将這個$proxy0類強制轉型成xiangqininterface類型,調用xiangqin方法。
網上看到一個牛人讀過這個動态代理的源代碼,現把網址留在這裡,希望對網友們有幫助。
<a target="_blank" href="http://hi.baidu.com/malecu/blog/item/45d4952b31bc0e27d52af17a.html">http://hi.baidu.com/malecu/blog/item/45d4952b31bc0e27d52af17a.html</a>
在目前的java開發包中包含了對動态代理的支援,但是其實作隻支援對接口的的實作。
其實作主要通過是java.lang.reflect.proxy類和java.lang.reflect.invocationhandler接口。
proxy類主要用來擷取動态代理對象,invocationhandler接口用來限制調用者實作,如下,diaosi接口定義的 業務方法,diaosinanimpl是diaosi接口的實作,
diaosinanhandler是 invocationhandler接口實作。代碼如下:
業務接口:
package proxy;
public interface diaosi {
void luguan();
業務接口實作:
public class diaosinan implements diaosi {
@override
public void luguan() {
// todo auto-generated method stub
system.out.println("我是撸管王");
}
invocationhandler實作,需要在接口方法調用前後加入一部份處理工作,這裡僅僅在方法調用前後向背景輸出兩句字元串,其代碼如下:
public class diaosinanhander implements invocationhandler{
//要代理的原始對象
private object diaosinan;
* 構造函數。
* @param obj 要代理的原始對象。
*/
public diaosinanhander(object diaosinan) {
this.diaosinan = diaosinan;
public object invoke(object proxy, method method, object[] args)
throws throwable {
object result ;
dobefore();
//調用原始對象的方法
result = method.invoke(this.diaosinan ,args);
//方法調用之後
doafter();
return result;
private void doafter() {
system.out.println("after method invoke!");
private void dobefore() {
system.out.println("before method invoke!");
測試代碼:
public class test {
diaosi ds = new diaosinan();
diaosinanhander handler = new diaosinanhander(ds);
diaosi proxy = (diaosi) proxy.newproxyinstance(
ds.getclass().getclassloader(),
ds.getclass().getinterfaces(),
handler);
/*public static object newproxyinstance(classloader loader,
class<?>[] interfaces,
invocationhandler h)
throws illegalargumentexception
{
if (h == null) {
throw new nullpointerexception();
}*/
proxy.luguan();
Ø 首先擷取一個業務接口的實作對象;
Ø 擷取一個invocationhandler實作,此處是
diaosinanhanderhandler對象;
Ø 建立動态代理對象;
Ø 通過動态代理對象調用luguan()方法,此時會在原始對象diaosinan.luguan()方法前後輸出兩句字元串。
運作測試類輸出如下:
before method invoke!
我試撸管王
after method invoke!
此處test類中的方法調用代碼比較多,在我們的實際應用中可以通過配置檔案來來簡化用戶端的調用實作。另外也可以通過動态代理來實作簡單的aop。
====================================分割線================================
最新内容請見作者的github頁:http://qaseven.github.io/