Java基礎知識:Java動態代理
很多地方都可以看到動態代理的影子,只是一直沒仔細看下。
在學習之前,先提出幾個問題,帶著問題來看代碼:
1.什么是動態代理?
2.為什么使用動態代理?
3.使用它有哪些好處?
4.哪些地方需要動態代理?
熟悉設計模式的人對于代理模式可 能都不陌生。 代理對象和被代理對象一般實現相同的接口,調用者與代理對象進行交互。代理的存在對于調用者來說是透明的,調用者看到的只是接口。代理對象則可以封裝一些內部的處理邏輯,如訪問控制、遠程通信、日志、緩存等。比如一個對象訪問代理就可以在普通的訪問機制之上添加緩存的支持。這種模式在RMI和EJB中都得到了廣泛的使用。傳統的代理模式的實現,需要在源代碼中添加一些附加的類。這些類一般是手寫或是通過工具來自動生成。JDK 5引入的動態代理機制,允許開發人員在運行時刻動態的創建出代理類及其對象。在運行時刻,可以動態創建出一個實現了多個接口的代理類。每個代理類的對象都會關聯一個表示內部處理邏輯的InvocationHandler接 口的實現。當使用者調用了代理對象所代理的接口中的方法的時候,這個調用的信息會被傳遞給InvocationHandler的invoke方法。在 invoke方法的參數中可以獲取到代理對象、方法對應的Method對象和調用的實際參數。invoke方法的返回值被返回給使用者。這種做法實際上相 當于對方法調用進行了攔截。熟悉AOP的人對這種使用模式應該不陌生。但是這種方式不需要依賴AspectJ等AOP框架。
動態代理可以提供對另一個對象的訪問,同時隱藏實際對象的具體事實,代理對象對客戶隱藏了實際對象。動態代理可以對請求進行其他的一些處理,在不允許直接訪問某些類,
或需要對訪問做一些特殊處理等,這時候可以考慮使用代理。目前 Java 開發包中提供了對動態代理的支持,但現在只支持對接口的實現。
主要是通過 ng.reflect.Proxy 類和 ng.reflect.InvocationHandler 接口。 Proxy 類主要用來獲取動態代理對象,InvocationHandler 接口用來約束調用者行為。
"寫一個 ArrayList 類的代理,其內部實現和 ArrayList 中完全相同的功能,并可以計算每個方法運行的時間。"這是一份考題上的題目,沒有答案,來看下實現:
[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代理,通過代理計算每個方法調用所需時間
List<Integer> arrayListProxy = (List<Integer>)Proxy.newProxyInstance(
ArrayList.class.getClassLoader(), /*定義代理類的類加載器,用于創建代理對象,不一定必須是ArrayList,也可以是其他的類加載器*/
ArrayList.class.getInterfaces(), /*代理類要實現的接口列表*/
new InvocationHandler() { /*指派方法調用的調用處理程序,這里用了匿名內部類*/
private ArrayList<Integer> target = new ArrayList<Integer>(); //目標對象(真正操作的對象)
/**
* <B>方法描述:</B>
* <p style="margin-left:20px;color:#A52A2A;">
* 在代理實例上處理方法調用并返回結果
* @param proxy 代理對象(注意不是目標對象)
* @param method 被代理的方法
* @param args 被代理的方法的參數集
* @return <span style="color: #008080;"> 返回方法調用結果 </span>
*/
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; //返回實際調用的方法的返回值
}
}
);
arrayListProxy.add(2);
arrayListProxy.add(4);
System.out.println("--------- 迭代 ---------");
for(int i : arrayListProxy){
System.out.print(i + "t");
}
}
}從代碼上來看,用到了匿名內部類,這樣一來,InvocationHandler 只能用一次,如果多個地方都需要用到這樣一個相同的 InvocationHandler,可以將其抽象出來成為一個單獨的類:
[java]
package test;
import ng.reflect.InvocationHandler;
import thod;
import ncurrent.TimeUnit;
public class MyInvocationHandler implements InvocationHandler{
private Object target; //目標對象
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;
}
}
客戶端調用改成:
[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代理,通過代理計算每個方法調用所需時間
List<Integer> arrayListProxy = (List<Integer>)Proxy.newProxyInstance(
ArrayList.class.getClassLoader(), /*定義代理類的類加載器,用于創建代理對象,不一定必須是ArrayList,也可以是其他的類加載器*/
ArrayList.class.getInterfaces(), /*代理類要實現的接口列表*/
new MyInvocationHandler(new ArrayList<Integer>()) /*指派方法調用的調用處理程序,這里用了匿名內部類*/
);
arrayListProxy.add(2);
arrayListProxy.add(4);
System.out.println("--------- 迭代 ---------");
for(int i : arrayListProxy){
System.out.print(i + "t");
}
}
}從上面代碼看來,客戶端知道代理的實際目標對象,還知道怎么樣去創建這樣一個代理對象,如果想把這些信息全部對客戶端隱藏起來,可以將這些代碼挪到一個類中,將它們封裝起來:
[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;
}
});
}
}
}
客戶端調用改成:
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,如果不想用枚舉,就改用普通類來實現就行了。
總結:
回答以下問題:
1.什么是動態代理?
答:Java動態代理類位于ng.reflect包下,一般主要涉及到以下兩個類:
一、Interface InvocationHandler:該接口中僅定義了一個方法Object:invoke(Object obj,Method method,J2EEjava語言JDK1.4APIjavalangObject.html">Object[] args)。在實際使用時,第一個參數obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的參數數組。這個抽象方法在代理類中動態實現。
二、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,由它接管實際的工作。
2.為什么使用動態代理?
動態代理可以提供對另一個對象的訪問,同時隱藏實際對象的具體事實,代理對象對客戶隱藏了實際對象。動態代理可以對請求進行其他的一些處理,在不允許直接訪問某些類,