随笔-126  评论-247  文章-5  trackbacks-0

动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实,代理对象对客户隐藏了实际对象。动态代理可以对请求进行其他的一些处理,在不允许直接访问某些类,

或需要对访问做一些特殊处理等,这时候可以考虑使用代理。目前 Java 开发包中提供了对动态代理的支持,但现在只支持对接口的实现。
 
主要是通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口。 Proxy 类主要用来获取动态代理对象,InvocationHandler 接口用来约束调用者行为。

“写一个 ArrayList 类的代理,其内部实现和 ArrayList 中完全相同的功能,并可以计算每个方法运行的时间。”这是一份考题上的题目,没有答案,来看下实现:


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代理,通过代理计算每个方法调用所需时间
        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>
                 
*/
                @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;   //返回实际调用的方法的返回值
                    
                }
                
            }
        );
        arrayListProxy.add(
2);
        arrayListProxy.add(
4);
        System.out.println(
"--------- 迭代 ---------");
        
for(int i : arrayListProxy){
            System.out.print(i 
+ "\t");
        }
    }
}

后台打印输出结果:


[add] spend 
2 ms
[add] spend 
1 ms
--------- 迭代 ---------
[iterator] spend 
1 ms
2    4    

从代码上来看,用到了匿名内部类,这样一来,InvocationHandler 只能用一次,如果多个地方都需要用到这样一个相同的 InvocationHandler,可以将其抽象出来成为一个单独的类:


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; //目标对象
    
    
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;
        
    }

}


客户端调用改成:


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代理,通过代理计算每个方法调用所需时间
        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");
        }
    }
}

从上面代码看来,客户端知道代理的实际目标对象,还知道怎么样去创建这样一个代理对象,如果想把这些信息全部对客户端隐藏起来,可以将这些代码挪到一个类中,将它们封装起来:


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;
                            
                        }
                    });
        }
    }
}

客户端调用改成:


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,如果不想用枚举,就改用普通类来实现就行了。


  
posted on 2012-08-27 20:43 fancydeepin 阅读(6414) 评论(4)  编辑  收藏

评论:
# re: java 动态代理(Proxy) 2012-08-28 11:47 | 菠萝大象
我想问下,你用枚举是为了什么?  回复  更多评论
  
# re: java 动态代理(Proxy) 2012-08-28 19:14 | fancydeepin
回复:@菠萝大象

枚举列表的每个元素都是一个枚举对象,它们是静态的,枚举的构造子默认是只能内部可用,私有的,这样一来,防止了外部试图自己创建对象,取得代理对象只需直接调用 getInstance() 方法而无需自己去创建对象,一般情况下,我们会提供一个静态工厂方法,用来获得代理对象,在这里因为目标很明确,就是要实现一个 ArrayList 的代理,所以在这里我用枚举取代之,并不使用静态工厂方法,或类成员,或类方法,或成员方法,用这些都能够取得代理对象,这里的实现只是我的一点想法,如果做的不好的话,请多指教
  回复  更多评论
  
# re: java 动态代理(Proxy) 2012-10-08 23:43 | distinys
我想知道怎么关注你。。找不到加关注几个字啊。。  回复  更多评论
  
# re: java 动态代理(Proxy) 2012-10-11 15:20 | fancydeepin
回复 @distinys

很抱歉,我也没找得到"关注"二字,感谢你的访问 ^_^  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航:
博客园   IT新闻   Chat2DB   C++博客   博问