Java动态代理(Proxy)

  动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实,代理对象对客户隐藏了实际对象。动态代理可以对请求进行其他的一些处理,在不允许直接访问某些类,或需要对访问做一些特殊处理等,这时候可以考虑使用代理。目前 Java 开发包中提供了对动态代理的支持,但现在只支持对接口的实现。

  主要是通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口。 Proxy 类主要用来获取动态代理对象,InvocationHandler 接口用来约束调用者行为。

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

  

    packageexample; importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; importjava.lang.reflect.Proxy; importjava.util.ArrayList; importjava.util.List; importncurrent.TimeUnit; /***—————————————–*@描述TODO*@作者fancy*@邮箱*@日期2012-8-27<p>*—————————————–*/publicclassProxyApp{ publicstaticvoidmain(String[]args){ //ArrayList代理,通过代理计算每个方法调用所需时间List<Integer>arrayListProxy=(List<Integer>)Proxy.newProxyInstance( ArrayList.class.getClassLoader(),/*定义代理类的类加载器,用于创建代理对象,不一定必须是ArrayList,也可以是其他的类加载器*/ArrayList.class.getInterfaces(),/*代理类要实现的接口列表*/newInvocationHandler(){/*指派方法调用的调用处理程序,这里用了匿名内部类*/ privateArrayList<Integer>target=newArrayList<Integer>();//目标对象(真正操作的对象)/***<B>方法描述:</B>*<p>*在代理实例上处理方法调用并返回结果*@paramproxy代理对象(注意不是目标对象)*@parammethod被代理的方法*@paramargs被代理的方法的参数集*@return<span>返回方法调用结果</span>*/@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{ longbeginTime=System.currentTimeMillis();//开始时间TimeUnit.MICROSECONDS.sleep(1); Objectobj=method.invoke(target,args);//实际调用的方法,并接受方法的返回值longendTime=System.currentTimeMillis();//结束时间System.out.println(“[“+method.getName()+”]spend”+(endTime-beginTime)+”ms”); returnobj;//返回实际调用的方法的返回值 } } ); arrayListProxy.add(2); arrayListProxy.add(4); System.out.println(“———迭代———“); for(inti:arrayListProxy){ System.out.print(i+”\t”); } } }

  后台打印输出结果:

  [add] spend 2 ms[add] spend 1 ms——— 迭代 ———[iterator] spend 1 ms2 4

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

  

    packagetest; importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; importncurrent.TimeUnit; publicclassMyInvocationHandlerimplementsInvocationHandler{ privateObjecttarget;//目标对象 publicMyInvocationHandler(Objecttarget){ this.target=target; } @OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{ longbeginTime=System.currentTimeMillis(); TimeUnit.MICROSECONDS.sleep(1); Objectobj=method.invoke(target,args); longendTime=System.currentTimeMillis(); System.out.println(“[“+method.getName()+”]spend”+(endTime-beginTime)+”ms”); returnobj; } }

  客户端调用改成:

  

    packageexample; importjava.lang.reflect.Proxy; importjava.util.ArrayList; importjava.util.List; /** *—————————————– *@描述TODO *@作者fancy *@邮箱 *@日期2012-8-27<p> *—————————————– */publicclassProxyApp{ publicstaticvoidmain(String[]args){ //ArrayList代理,通过代理计算每个方法调用所需时间 List<Integer>arrayListProxy=(List<Integer>)Proxy.newProxyInstance( ArrayList.class.getClassLoader(),/*定义代理类的类加载器,用于创建代理对象,不一定必须是ArrayList,也可以是其他的类加载器*/ArrayList.class.getInterfaces(),/*代理类要实现的接口列表*/newMyInvocationHandler(newArrayList<Integer>())/*指派方法调用的调用处理程序,这里用了匿名内部类*/); arrayListProxy.add(2); arrayListProxy.add(4); System.out.println(“———迭代———“); for(inti:arrayListProxy){ System.out.print(i+”\t”); } } }

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

  

    packageexample; importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; importjava.lang.reflect.Proxy; importjava.util.ArrayList; importjava.util.List; importncurrent.TimeUnit; /** *—————————————– *@描述TODO *@作者fancy *@邮箱 *@日期2012-8-27<p> *—————————————– */publicclassProxyUtil{ publicenumArrayListProxy{ PROXY; privateObjecttarget; ArrayListProxy(){ this.target=newArrayList<Object>(); } publicListgetInstance(){ return(List)Proxy.newProxyInstance(ArrayList.class.getClassLoader(),ArrayList.class.getInterfaces(), newInvocationHandler(){ @OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{ longbeginTime=System.currentTimeMillis(); TimeUnit.MICROSECONDS.sleep(1); Objectobj=method.invoke(target,args); longendTime=System.currentTimeMillis(); System.out.println(“[“+method.getName()+”]spend”+(endTime-beginTime)+”ms”); returnobj; } }); } } }

  客户端调用改成:

  

    packageexample; importjava.util.List; importexample.ProxyUtil.ArrayListProxy; /** *—————————————– *@描述TODO *@作者fancy *@邮箱 *@日期2012-8-27<p> *—————————————– */publicclassProxyApp{ publicstaticvoidmain(String[]args){ List<Integer>arrayListProxy=ArrayListProxy.PROXY.getInstance(); arrayListProxy.add(2); arrayListProxy.add(4); System.out.println(“———迭代———“); for(inti:arrayListProxy){ System.out.print(i+”\t”); } } }

  上面代码中用到了枚举 enum,如果不想用枚举,就改用普通类来实现就行了。

始终调整好自己观风景的心态,

Java动态代理(Proxy)

相关文章:

你感兴趣的文章:

标签云: