视窗
loading...
您现在的位置:首页 > IT认证 > JAVA >

Java基础知识:Java动态代理


没事的时候翻看lang.reflect包下的代码,发现有两部分内容:涉及反射和动态代理。

    很多地方都可以看到动态代理的影子,只是一直没仔细看下。

    在学习之前,先提出几个问题,带着问题来看代码:

    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.为什么使用动态代理?

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

闂備線娼уΛ宀勫磻閿燂拷

闂備線娼уΛ宀勫磻閿燂拷

闂備線娼уΛ娆撳礉閺囥垹鍌ㄩ柕鍫濇处鐎氬鏌ㄥ┑鍡樺珔缂佹唻缍侀弻锟犲礋椤愶富鈧鏌熼摎鍌氬祮闁诡啫鍥ч唶闁绘柨鎽滅粔顒勬煟閻樺弶鎼愰柣掳鍔屽嵄闁硅揪绠戣繚闂佽法鍣﹂幏锟�
闂備礁鎼悧婊堝礈濞戙垺鍋熸い鏍仦閻掗箖鏌曟繛鍨姎闁诲氦顕ц彁闁搞儻绲芥晶鎻捗归悡搴㈠殗鐎殿喖鐖兼俊鐑芥晜閸撗冪厓濠电偛鐡ㄧ划宀€鑺遍懖鈺勫С濞寸厧鐡ㄩ崵鍌炴煛閸愩劌鈧崵绮旇ぐ鎺撶叆婵炴垼娅曠€氾拷闂佽娴烽弫鎼併€佹繝鍋綊宕卞Ο璇差潯闂佷紮绲介張顒勬偩閸楃們搴ㄥ炊閿濆懎鈷夋繛瀵稿帶閹虫﹢鐛€n喖绠f繝濠傚閹枫劑姊洪崨濠冣拹缂佸甯¢幆鍥ㄥ閺夋垵鍞ㄩ梺鎼炲劘閸斿秹锝為弽顬ュ酣宕堕敐鍛拤婵炲鍘ч幊姗€骞嗛崘顔肩妞ゆ劑鍨洪惁鏃€绻濋姀锝嗙【閻庢艾鎽滃Σ鎰版晸閿燂拷闂備胶鎳撻悺銊╁垂閸愭祴鍫柟瀵稿С閻掑﹤鈹戦悩鍙夋悙婵炲懌鍨归湁闁挎繂妫涢惌搴ㄦ煃瑜滈崜娆撳箠閹邦兘鏋旈柟杈鹃檮閸嬪鏌涢銈呮瀾缂傚秮鍋撻梻浣瑰灊閻掞箓濡甸悙鐢电闁哄啫鐗嗙痪褔鏌涢幇顖涚《缂佲偓閿燂拷闂佽绨肩徊濠氾綖婢舵劕鍨傛繝濠傚椤╅攱銇勯幒鎴濇殲缂佷緡鍣e鍫曟倷閸偅鐝┑鐐茬墛閸ㄥ墎绮氶柆宥呯労闁告剬鍛槬濠电姷顣介埀顒€鍟块埀顒傛嚀閿曘垺鎷呴崜鎻掓闂佺ǹ鏈换宥夊船閹绢喗鐓欓悗娑欋缚婢ь剚绻濋埀顒佹媴閸︻厾鎳濋梺鍓茬厛閸嬪懐绱為埀顒勬⒑閻熸壆鎽犻柣妤冨仧閹广垹顫濋鑺ョ亙闂佸搫娲﹂惌顔炬崲閸℃稒鐓欐い鎾楀啰浠村銈嗘处閸撶喎鐣烽敐鍡欑瘈闁告劏鏅╁Σ顖炴⒑閼逛即鍝烘慨濠傤煼閺屽牓骞橀鑲╊吅闂佺懓鐡ㄧ划宥囧垝閿曞倹鐓ユ繛鎴炆戝﹢鐗堢節閳ь剟骞嶉鎯у触濠电偛妫楀ù椋庣玻濡ゅ啰纾奸柡鍌涱儥閸庡繒鈧鎸稿Λ婵嗙暦濮樿埖鍋愮紓浣贯缚瑜版垿姊洪幐搴″枙闁瑰嚖鎷�闂佽娴烽弫鎼佸箠閹捐埖鏆滄い鎰剁畱缁€鍡樼箾閹寸伝顏堝极閸洘鍊电痪顓炴媼濞兼劙鏌涢妸锔剧煁缂佸倹甯¢、妤佹媴缁嬪晝顐︽⒑鐟欏嫭绶茬紒缁樺灴瀵偊顢欓悾宀婃祫濠殿喗銇涢崑鎾绘煃瑜滈崜娆撳磹閸濄儳绀婇悗锝庡枟閸庡秹鏌涢弴銊ュ笌鐟滅増甯楅悡鈧銈嗗笒閿曪妇绮堥敓锟�闂備浇澹堟ご绋款潖婵犳碍鐒鹃柟缁㈠枛濡﹢鏌i悢绋款棆缁绢厸鍋撻梻浣瑰缁嬫帒鐣濋幖浣哥;闁哄秲鍔庨々鐑芥煥閻曞倹瀚�:webmaster@jscj.com闂備線娼уΛ宀勫磻閹剧粯鐓熸い顐幘缁佺兘鏌i敂璺ㄧ煓闁轰礁绉归弫鎾绘晸閿燂拷4008816886

相关文章

无相关信息
更新时间2022-09-16 10:07:22【至顶部↑】
联系我们 | 邮件: webmaster@jscj.com | 客服热线电话:4008816886(QQ同号) |  婵犵數鍎戠紞鈧い鏇嗗嫭鍙忛柣鎰暯閸嬫捇鐛崹顔句痪濠电姭鍋撻柛銉戝苯娈銈嗘椤斿﹦鎹㈤敓锟�

付款方式留言簿投诉中心网站纠错二维码手机版

电话:
付款方式   |   给我留言   |   我要纠错   |   联系我们