Java基礎(chǔ)知識(shí):Java動(dòng)態(tài)代理
很多地方都可以看到動(dòng)態(tài)代理的影子,只是一直沒仔細(xì)看下。
在學(xué)習(xí)之前,先提出幾個(gè)問題,帶著問題來看代碼:
1.什么是動(dòng)態(tài)代理?
2.為什么使用動(dòng)態(tài)代理?
3.使用它有哪些好處?
4.哪些地方需要?jiǎng)討B(tài)代理?
熟悉設(shè)計(jì)模式的人對(duì)于代理模式可 能都不陌生。 代理對(duì)象和被代理對(duì)象一般實(shí)現(xiàn)相同的接口,調(diào)用者與代理對(duì)象進(jìn)行交互。代理的存在對(duì)于調(diào)用者來說是透明的,調(diào)用者看到的只是接口。代理對(duì)象則可以封裝一些內(nèi)部的處理邏輯,如訪問控制、遠(yuǎn)程通信、日志、緩存等。比如一個(gè)對(duì)象訪問代理就可以在普通的訪問機(jī)制之上添加緩存的支持。這種模式在RMI和EJB中都得到了廣泛的使用。傳統(tǒng)的代理模式的實(shí)現(xiàn),需要在源代碼中添加一些附加的類。這些類一般是手寫或是通過工具來自動(dòng)生成。JDK 5引入的動(dòng)態(tài)代理機(jī)制,允許開發(fā)人員在運(yùn)行時(shí)刻動(dòng)態(tài)的創(chuàng)建出代理類及其對(duì)象。在運(yùn)行時(shí)刻,可以動(dòng)態(tài)創(chuàng)建出一個(gè)實(shí)現(xiàn)了多個(gè)接口的代理類。每個(gè)代理類的對(duì)象都會(huì)關(guān)聯(lián)一個(gè)表示內(nèi)部處理邏輯的InvocationHandler接 口的實(shí)現(xiàn)。當(dāng)使用者調(diào)用了代理對(duì)象所代理的接口中的方法的時(shí)候,這個(gè)調(diào)用的信息會(huì)被傳遞給InvocationHandler的invoke方法。在 invoke方法的參數(shù)中可以獲取到代理對(duì)象、方法對(duì)應(yīng)的Method對(duì)象和調(diào)用的實(shí)際參數(shù)。invoke方法的返回值被返回給使用者。這種做法實(shí)際上相 當(dāng)于對(duì)方法調(diào)用進(jìn)行了攔截。熟悉AOP的人對(duì)這種使用模式應(yīng)該不陌生。但是這種方式不需要依賴AspectJ等AOP框架。
動(dòng)態(tài)代理可以提供對(duì)另一個(gè)對(duì)象的訪問,同時(shí)隱藏實(shí)際對(duì)象的具體事實(shí),代理對(duì)象對(duì)客戶隱藏了實(shí)際對(duì)象。動(dòng)態(tài)代理可以對(duì)請(qǐng)求進(jìn)行其他的一些處理,在不允許直接訪問某些類,
或需要對(duì)訪問做一些特殊處理等,這時(shí)候可以考慮使用代理。目前 Java 開發(fā)包中提供了對(duì)動(dòng)態(tài)代理的支持,但現(xiàn)在只支持對(duì)接口的實(shí)現(xiàn)。
主要是通過 ng.reflect.Proxy 類和 ng.reflect.InvocationHandler 接口。 Proxy 類主要用來獲取動(dòng)態(tài)代理對(duì)象,InvocationHandler 接口用來約束調(diào)用者行為。
"寫一個(gè) ArrayList 類的代理,其內(nèi)部實(shí)現(xiàn)和 ArrayList 中完全相同的功能,并可以計(jì)算每個(gè)方法運(yùn)行的時(shí)間。"這是一份考題上的題目,沒有答案,來看下實(shí)現(xiàn):
[java]
package proxy;
import ng.reflect.InvocationHandler;
import thod;
import ng.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import ncurrent.TimeUnit;
public class ProxyApp {
public static void main(String[] args){ //ArrayList代理,通過代理計(jì)算每個(gè)方法調(diào)用所需時(shí)間
List<Integer> arrayListProxy = (List<Integer>)Proxy.newProxyInstance(
ArrayList.class.getClassLoader(), /*定義代理類的類加載器,用于創(chuàng)建代理對(duì)象,不一定必須是ArrayList,也可以是其他的類加載器*/
ArrayList.class.getInterfaces(), /*代理類要實(shí)現(xiàn)的接口列表*/
new InvocationHandler() { /*指派方法調(diào)用的調(diào)用處理程序,這里用了匿名內(nèi)部類*/
private ArrayList<Integer> target = new ArrayList<Integer>(); //目標(biāo)對(duì)象(真正操作的對(duì)象)
/**
* <B>方法描述:</B>
* <p style="margin-left:20px;color:#A52A2A;">
* 在代理實(shí)例上處理方法調(diào)用并返回結(jié)果
* @param proxy 代理對(duì)象(注意不是目標(biāo)對(duì)象)
* @param method 被代理的方法
* @param args 被代理的方法的參數(shù)集
* @return <span style="color: #008080;"> 返回方法調(diào)用結(jié)果 </span>
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long beginTime = System.currentTimeMillis(); //開始時(shí)間
TimeUnit.MICROSECONDS.sleep(1);
Object obj = method.invoke(target, args); //實(shí)際調(diào)用的方法,并接受方法的返回值
long endTime = System.currentTimeMillis(); //結(jié)束時(shí)間
System.out.println("[" + method.getName() + "] spend " + (endTime - beginTime) + " ms");
return obj; //返回實(shí)際調(diào)用的方法的返回值
}
}
);
arrayListProxy.add(2);
arrayListProxy.add(4);
System.out.println("--------- 迭代 ---------");
for(int i : arrayListProxy){
System.out.print(i + "t");
}
}
}從代碼上來看,用到了匿名內(nèi)部類,這樣一來,InvocationHandler 只能用一次,如果多個(gè)地方都需要用到這樣一個(gè)相同的 InvocationHandler,可以將其抽象出來成為一個(gè)單獨(dú)的類:
[java]
package test;
import ng.reflect.InvocationHandler;
import thod;
import ncurrent.TimeUnit;
public class MyInvocationHandler implements InvocationHandler{
private Object target; //目標(biāo)對(duì)象
public MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long beginTime = System.currentTimeMillis();
TimeUnit.MICROSECONDS.sleep(1);
Object obj = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println("[" + method.getName() + "] spend " + (endTime - beginTime) + " ms");
return obj;
}
}
客戶端調(diào)用改成:
[java]
package example;
import ng.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
/**
* -----------------------------------------
* -----------------------------------------
*/
public class ProxyApp {
public static void main(String[] args){
//ArrayList代理,通過代理計(jì)算每個(gè)方法調(diào)用所需時(shí)間
List<Integer> arrayListProxy = (List<Integer>)Proxy.newProxyInstance(
ArrayList.class.getClassLoader(), /*定義代理類的類加載器,用于創(chuàng)建代理對(duì)象,不一定必須是ArrayList,也可以是其他的類加載器*/
ArrayList.class.getInterfaces(), /*代理類要實(shí)現(xiàn)的接口列表*/
new MyInvocationHandler(new ArrayList<Integer>()) /*指派方法調(diào)用的調(diào)用處理程序,這里用了匿名內(nèi)部類*/
);
arrayListProxy.add(2);
arrayListProxy.add(4);
System.out.println("--------- 迭代 ---------");
for(int i : arrayListProxy){
System.out.print(i + "t");
}
}
}從上面代碼看來,客戶端知道代理的實(shí)際目標(biāo)對(duì)象,還知道怎么樣去創(chuàng)建這樣一個(gè)代理對(duì)象,如果想把這些信息全部對(duì)客戶端隱藏起來,可以將這些代碼挪到一個(gè)類中,將它們封裝起來:
[java]
package example;
import ng.reflect.InvocationHandler;
import thod;
import ng.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import ncurrent.TimeUnit;
/**
* -----------------------------------------
* -----------------------------------------
*/
public class ProxyUtil {
public enum ArrayListProxy {
PROXY;
private Object target;
ArrayListProxy(){
this.target = new ArrayList<Object>();
}
public List getInstance(){
return (List)Proxy.newProxyInstance(ArrayList.class.getClassLoader(), ArrayList.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long beginTime = System.currentTimeMillis();
TimeUnit.MICROSECONDS.sleep(1);
Object obj = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println("[" + method.getName() + "] spend " + (endTime - beginTime) + " ms");
return obj;
}
});
}
}
}
客戶端調(diào)用改成:
package example;
import java.util.List;
import example.ProxyUtil.ArrayListProxy;
/**
* -----------------------------------------
------------------
*/
public class ProxyApp {
public static void main(String[] args){
List<Integer> arrayListProxy = ArrayListProxy.PROXY.getInstance();
arrayListProxy.add(2);
arrayListProxy.add(4);
System.out.println("--------- 迭代 ---------");
for(int i : arrayListProxy){
System.out.print(i + "t");
}
}
}
上面代碼中用到了枚舉 enum,如果不想用枚舉,就改用普通類來實(shí)現(xiàn)就行了。
總結(jié):
回答以下問題:
1.什么是動(dòng)態(tài)代理?
答:Java動(dòng)態(tài)代理類位于ng.reflect包下,一般主要涉及到以下兩個(gè)類:
一、Interface InvocationHandler:該接口中僅定義了一個(gè)方法Object:invoke(Object obj,Method method,J2EEjava語言JDK1.4APIjavalangObject.html">Object[] args)。在實(shí)際使用時(shí),第一個(gè)參數(shù)obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的參數(shù)數(shù)組。這個(gè)抽象方法在代理類中動(dòng)態(tài)實(shí)現(xiàn)。
二、Proxy:該類即為動(dòng)態(tài)代理類,作用類似于上例中的ProxySubject,其中主要包含以下內(nèi)容:
Protected Proxy(InvocationHandler h):構(gòu)造函數(shù),估計(jì)用于給內(nèi)部的h賦值。
Static Class getProxyClass (ClassLoader loader,Class[] interfaces):獲得一個(gè)代理類,其中l(wèi)oader是類裝載器,interfaces是真實(shí)類所擁有的全部接口的數(shù)組。
Static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h):返回代理類的一個(gè)實(shí)例,返回后的代理類可以當(dāng)作被代理類使用(可使用被代理類的在Subject接口中聲明過的方法)。
所謂Dynamic Proxy是這樣一種class:它是在運(yùn)行時(shí)生成的class,在生成它時(shí)你必須提供一組interface給它,然后該class就宣稱它實(shí)現(xiàn)了這些 interface.你當(dāng)然可以把該class的實(shí)例當(dāng)作這些interface中的任何一個(gè)來用。當(dāng)然啦,這個(gè)Dynamic Proxy其實(shí)就是一個(gè)Proxy,它不會(huì)替你作實(shí)質(zhì)性的工作,在生成它的實(shí)例時(shí)你必須提供一個(gè)handler,由它接管實(shí)際的工作。
2.為什么使用動(dòng)態(tài)代理?
動(dòng)態(tài)代理可以提供對(duì)另一個(gè)對(duì)象的訪問,同時(shí)隱藏實(shí)際對(duì)象的具體事實(shí),代理對(duì)象對(duì)客戶隱藏了實(shí)際對(duì)象。動(dòng)態(tài)代理可以對(duì)請(qǐng)求進(jìn)行其他的一些處理,在不允許直接訪問某些類,