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

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

闂傚倷绶氬ḿ褍螞瀹€鍕;闁跨噦鎷�

闂傚倷绶氬ḿ褍螞瀹€鍕;闁跨噦鎷�

闂傚倷绶氬ḿ褍螞濞嗘挸绀夐柡鍥ュ灩閸屻劑鏌曢崼婵囧閻庢艾顭烽弻銊モ攽閸℃ê鐝旂紓浣瑰敾缂嶄線寮婚敓鐘茬妞ゆ劧瀵岄埀顒侇殜閺岀喖鎽庨崒姘ギ闂佽鍟崶褔鍞堕梺缁樻煥閹芥粎绮旈鍕厽闁绘ê寮堕幖鎰版煟鎺抽崝灞藉祫闂佺鎻粻鎴g箽闂備浇娉曢崳锕傚箯閿燂拷
闂傚倷绀侀幖顐︽偋濠婂牆绀堟繛鎴欏灪閸嬬喐銇勯弽顐沪闁绘帡绠栭弻鏇熺箾閸喖濮庨梺璇叉唉椤曆嗗絹闂佹悶鍎荤徊鑺ユ櫠閹绘崡褰掓偂鎼淬垹娈楅悗娈垮枛閻栧吋淇婇悜鑺ユ櫆闁告挆鍐帗婵犵數鍋涢悺銊у垝瀹€鈧懞閬嶆嚃閳哄嫬小婵炲鍘ч悺銊╁吹閸岀偞鐓涢柛鎰╁妼閳ь剝宕电划鏃囥亹閹烘挾鍙嗗┑鐐村灱濞呮洜鈧熬鎷�闂備浇顕уù鐑藉极閹间降鈧焦绻濋崑顖氱秺瀹曞崬螣鐠囧樊娼梻浣风串缁蹭粙寮甸鍕仼闁告鍊戞惔銊ョ倞闁挎繂鎳庨埛澶嬬箾鐎电ǹ甯堕柟铏耿閻涱喚鈧綆鍠栫粻锝嗙節婵犲倸顏柟鏋姂濮婃椽宕ㄦ繝鍐f嫻缂備礁顑嗙敮锟犲箚閸ャ劌顕遍柡澶嬪灥閸炪劑姊洪幖鐐插姌闁告柨绉归敐鐐哄冀椤儱閰e畷鍫曟晲閸涱厸鎷ゅ┑鐐差嚟閸樠囧箠濮椻偓楠炲棝宕橀鑲╊槹濡炪倖鍔戦崹娲儊閺冣偓缁绘繈濮€閿濆棛銆愰柣搴㈣壘閹芥粌危閹扮増鏅搁柨鐕傛嫹闂傚倷鑳堕幊鎾绘偤閵娾晛鍨傞柛鎰ゴ閸亪鏌熺€电ǹ小闁绘帒锕ら埞鎴︽偐閸欏鎮欏┑鐐叉噷閸ㄥ綊婀侀梺鎸庣箓濡盯鎯屾惔銊︾厓鐟滄粓宕滃▎鎾崇疇闁归偊鍏橀弸鏃堟煙鏉堥箖妾柛瀣儔閺屾盯顢曢妶鍛€剧紓鍌氱М閸嬫捇姊绘担鐟扮亰闁绘帪绠撴俊鐢告倷閻㈢數顦梺鍝勫暙閻楀棛鐥閺屾盯骞囬娑氥€婄紓浣插亾闁跨噦鎷�闂備浇顕х花鑲╁緤婵犳熬缍栧鑸靛姇閸ㄥ倹绻濇繝鍌氼伀妞も晠鏀遍妵鍕箳閹存繃娈茬紓浣风贰閸o絽顕i崼鏇熷€烽柛顭戝亝閻濐亜鈹戦悙鑼闁搞劌澧庣划姘舵焼瀹ュ懐鍔撮梺鍛婂壃閸涱垼妲繝鐢靛Х椤d粙鍩€椤掆偓閸熷潡鍩€椤掑倹鍤€闁挎洏鍨洪幏鍛村礈閹绘帗顓块梻浣呵归張顒傛崲瀹ュ鑸归柟缁㈠枟閻撴瑩鎮楀☉娆嬬細濠⒀屽墯缁绘繈鍩€椤掍焦濯撮柛锔诲幘閹虫繈姊洪崜鑼帥闁稿鎳愮槐鐐哄焵椤掑嫭鈷戦柣鐔稿閹界娀鏌eΔ鍐ㄤ户闁瑰箍鍨归~婵嬵敄閼恒儳浜欓梻浣告惈濞诧箓鎯岄鐐床闁糕剝绋掗悡娆愩亜閹炬鍟版禒鏉戭渻閵堝棙澶勯柛鎾跺枎閻g兘鏁愰崱娆戠槇闂佸憡鍔忛弲鈺佄i鐐粹拺闁奸€涘嵆閸濈儤鎱ㄦ繝鍌ょ吋闁哄苯鐗撻獮姗€顢欓懖鈺婂悈闂備胶鎳撻悺銊у垝瀹ュ洤鍨濋柨鏇炲€归悡銉︾箾閹寸倖鎴濓耿閻楀牏绡€闁逞屽墴楠炲秹顢欓幆褍瑙︽繝鐢靛仜濡霉妞嬪海鐜绘俊銈呭暟绾惧ジ鏌¢崒娑卞劌闁稿骸绻掗埀顒冾潐閹哥ǹ螞濠靛棛鏆︽慨妯垮煐閸嬫劗绱撴担璐細鐟滅増鍨垮娲箰鎼粹€虫灆闂佺懓鍤栭幏锟�闂備浇顕уù鐑藉极閹间礁绠犻柟鎹愬煐閺嗘粍銇勯幇鍓佺暠缂佲偓閸℃ḿ绠鹃柟瀵镐紳椤忓牆鏋侀柛顐f礃閸婄數鐥鐐村婵炲吋鍔欓弻娑㈠Ω閿斿墽鐓佺紓浣稿€圭敮锟犮€佸Δ浣瑰缂佸鏅濋锔解拺閻熸瑥瀚欢鑼磼缂佹ê鐏寸€殿噮鍋婇、娆撴偩瀹€濠冪カ婵犳鍠楅妵娑㈠磻閹剧粯鐓冪憸婊堝礈濞嗘挸纾归柛婵勫劤缁€濠囨倵閿濆骸鏋熼柛搴$Ч閺屾盯寮撮妸銉ョ瑢閻熸粎澧楃敮妤呮偂閳ь剙顪冮妶鍡楃瑨闁挎洩濡囩划鍫ユ晸閿燂拷闂傚倷娴囨竟鍫熴仈缁嬫娼栧┑鐘崇閻掗箖鏌熺紒銏犳灈婵☆偅锕㈤弻锝夋偄缁嬫妫嗙紒缁㈠幐閸嬫捇姊绘担鐟邦嚋缂佸甯掗悾婵嬪箹娴e摜锛涢梺鍝勭Р閸斿酣銆呴悜鑺ョ叆闁绘洖鍊圭€氾拷:webmaster@jscj.com闂傚倷绶氬ḿ褍螞瀹€鍕;闁瑰墽绮悡鐔搞亜椤愵偄骞樼紒浣哄厴閺岋綁鏁傜捄銊х厯闂佽桨绀佺粔褰掑极閹剧粯鏅搁柨鐕傛嫹4008816886

相关文章

无相关信息
更新时间2022-09-16 10:07:22【至顶部↑】
联系我们 | 邮件: webmaster@jscj.com | 客服热线电话:4008816886(QQ同号) |  濠电姷鏁告慨鐑藉极閸涘﹥鍙忛柣鎴f閺嬩線鏌熼梻瀵割槮缁炬儳娼¢弻鐔衡偓鐢登瑰暩缂備讲鍋撳┑鐘叉处閻撳啰鎲稿⿰鍫濈婵ǹ娉涙闂佸憡娲﹂崹浼村础閹惰姤鐓曢柍鈺佸枤濞堟梹銇勮箛锝勯偗闁哄本绋戦埢搴ょ疀閺囩媭鍟嬮梻浣告惈閻绱炴笟鈧悰顕€宕堕澶嬫櫔闂佸憡渚楅崰妤呮儊濠婂牊鈷掑ù锝呮嚈瑜版帩鏁勯柛鈩冪☉缁犳煡鏌涢妷顔煎闁稿蓱閵囧嫰寮村Δ鈧禍楣冩倵鐟欏嫭绀€缂傚秴锕妴浣糕枎閹惧磭鐓戦梺闈涳紡閸曞墎纭€婵犵數濮烽弫鍛婃叏閻戣棄鏋侀柛娑橈攻閸欏繑銇勯幘鍗炵仼缁炬儳顭烽弻鐔煎礈瑜忕敮娑㈡煟閹惧瓨绀嬮柡灞炬礃瀵板嫮鈧綆浜炴禒鎾⒑閸忓吋銇熼柛銊ゅ嵆閸┿垽骞樺ú缁樻櫇闂侀潧绻嗛弲婊呪偓闈涚焸濮婃椽妫冨☉姘暫濠碘槅鍋呴悷鈺勬"闂佺硶鍓濊彠濞存粍绮撻弻锟犲磼濮橆剛娈归梺鍛婃⒒閸犳牠寮婚悢鑲╁祦闁割煈鍠氭导鍫ユ⒑鏉炴壆鍔嶉柟鐟版喘瀵偊骞樼紒妯绘闂佽法鍣﹂幏锟�

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

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