Java 動(dòng)態(tài)代理(Proxy)
動(dòng)態(tài)代理可以提供對(duì)另一個(gè)對(duì)象的訪問(wèn),同時(shí)隱藏實(shí)際對(duì)象的具體事實(shí),代理對(duì)象對(duì)客戶隱藏了實(shí)際對(duì)象。動(dòng)態(tài)代理可以對(duì)請(qǐng)求進(jìn)行其他的一些處理,在不允許直接訪問(wèn)某些類(lèi),或需要對(duì)訪問(wèn)做一些特殊處理等,這時(shí)候可以考慮使用代理。目前 Java 開(kāi)發(fā)包中提供了對(duì)動(dòng)態(tài)代理的支持,但現(xiàn)在只支持對(duì)接口的實(shí)現(xiàn)。
主要是通過(guò) java.lang.reflect.Proxy 類(lèi)和 java.lang.reflect.InvocationHandler 接口。 Proxy 類(lèi)主要用來(lái)獲取動(dòng)態(tài)代理對(duì)象,InvocationHandler 接口用來(lái)約束調(diào)用者行為。
“寫(xiě)一個(gè) ArrayList 類(lèi)的代理,其內(nèi)部實(shí)現(xiàn)和 ArrayList 中完全相同的功能,并可以計(jì)算每個(gè)方法運(yùn)行的時(shí)間。”這是一份考題上的題目,沒(méi)有答案,來(lái)看下實(shí)現(xiàn):
- package example;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.concurrent.TimeUnit;
- /**
- * -----------------------------------------
- * @描述 TODO
- * @作者 fancy
- * @郵箱 fancydeepin@yeah.net
- * @日期 2012-8-27 <p>
- * -----------------------------------------
- */
- public class ProxyApp {
- public static void main(String[] args){
- //ArrayList代理,通過(guò)代理計(jì)算每個(gè)方法調(diào)用所需時(shí)間
- List<Integer> arrayListProxy = (List<Integer>)Proxy.newProxyInstance(
- ArrayList.class.getClassLoader(), /*定義代理類(lèi)的類(lèi)加載器,用于創(chuàng)建代理對(duì)象,不一定必須是ArrayList,也可以是其他的類(lèi)加載器*/
- ArrayList.class.getInterfaces(), /*代理類(lèi)要實(shí)現(xiàn)的接口列表*/
- new InvocationHandler() { /*指派方法調(diào)用的調(diào)用處理程序,這里用了匿名內(nèi)部類(lè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>
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- long beginTime = System.currentTimeMillis(); //開(kāi)始時(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");
- }
- }
- }
后臺(tái)打印輸出結(jié)果:
[add] spend 2 ms [add] spend 1 ms --------- 迭代 --------- [iterator] spend 1 ms 2 4 |
從代碼上來(lái)看,用到了匿名內(nèi)部類(lèi),這樣一來(lái),InvocationHandler 只能用一次,如果多個(gè)地方都需要用到這樣一個(gè)相同的 InvocationHandler,可以將其抽象出來(lái)成為一個(gè)單獨(dú)的類(lèi):
- package test;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.util.concurrent.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)用改成:
- package example;
- import java.lang.reflect.Proxy;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * -----------------------------------------
- * @描述 TODO
- * @作者 fancy
- * @郵箱 fancydeepin@yeah.net
- * @日期 2012-8-27 <p>
- * -----------------------------------------
- */
- public class ProxyApp {
- public static void main(String[] args){
- //ArrayList代理,通過(guò)代理計(jì)算每個(gè)方法調(diào)用所需時(shí)間
- List<Integer> arrayListProxy = (List<Integer>)Proxy.newProxyInstance(
- ArrayList.class.getClassLoader(), /*定義代理類(lèi)的類(lèi)加載器,用于創(chuàng)建代理對(duì)象,不一定必須是ArrayList,也可以是其他的類(lèi)加載器*/
- ArrayList.class.getInterfaces(), /*代理類(lèi)要實(shí)現(xiàn)的接口列表*/
- new MyInvocationHandler(new ArrayList<Integer>()) /*指派方法調(diào)用的調(diào)用處理程序,這里用了匿名內(nèi)部類(lèi)*/
- );
- arrayListProxy.add(2);
- arrayListProxy.add(4);
- System.out.println("--------- 迭代 ---------");
- for(int i : arrayListProxy){
- System.out.print(i + "\t");
- }
- }
- }
從上面代碼看來(lái),客戶端知道代理的實(shí)際目標(biāo)對(duì)象,還知道怎么樣去創(chuàng)建這樣一個(gè)代理對(duì)象,如果想把這些信息全部對(duì)客戶端隱藏起來(lái),可以將這些代碼挪到一個(gè)類(lèi)中,將它們封裝起來(lái):
- package example;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.concurrent.TimeUnit;
- /**
- * -----------------------------------------
- * @描述 TODO
- * @作者 fancy
- * @郵箱 fancydeepin@yeah.net
- * @日期 2012-8-27 <p>
- * -----------------------------------------
- */
- 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;
- /**
- * -----------------------------------------
- * @描述 TODO
- * @作者 fancy
- * @郵箱 fancydeepin@yeah.net
- * @日期 2012-8-27 <p>
- * -----------------------------------------
- */
- 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,如果不想用枚舉,就改用普通類(lèi)來(lái)實(shí)現(xiàn)就行了。
原文鏈接:http://www.blogjava.net/fancydeepin/archive/2012/08/27/java_proxy.html
【編輯推薦】