博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java 动态代理初步理解
阅读量:6306 次
发布时间:2019-06-22

本文共 8152 字,大约阅读时间需要 27 分钟。

hot3.png

别看小弟工作了一段时间,但对代理的理解一直不行,甚至根本就是懵逼状态。然后今天突然开窍。就赶紧记下一些理解。

java 的动态代理主要使用java.lang.reflect.Proxy。如果自己创建一个代理类的话,就需要自定义一个class实现invocationHandler,并重写invoke方法才行,这样自定义的class 才能在invoke中创建代理并调用被代理的方法。

而invocationHandler 接口只有一个invoke方法。就是给代理对象处理业务用的。

下面在说下Proxy,该对象主要负责创建代理,创建代理对象的源码如下:

@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,                                      Class
[] interfaces, InvocationHandler h) throws IllegalArgumentException{ Objects.requireNonNull(h); final Class
[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class
cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor
cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction
() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); }}

三个参数分别是:

1、代理对象的类加载器,通过调用getParent 发现是sun.misc.Launcher$ExtClassLoader@41629346

证明是APPClassLoader,系统级的类加载器

2、是一个class对象接口数组,该数组都是多态形式的,你可以传实现类的实体对象,但该方法返回的一定是对应多态的引用对象

3、代理类对象相关联的invocationHandler,是invocationHandler对象

好了。了解完参数。该怎么创建一个对象的动态代理对象呢?

步骤如下:

1、创建一个实体接口

package com.wisely.proxy;/** * DES:代理对象实体bean * Created by Reynole-白 * Date: 2017/8/26 16:27 */public interface Person {    void sing(String songName);    void dance(String danceName);}

2、创建其实现

package com.wisely.proxy;/** * DES: * Created by Reynole-白 * Date: 2017/8/26 16:29 */public class LiuDeHua implements Person {    public LiuDeHua(){        System.out.println("我是华仔的无参构造器");    }    @Override    public void sing(String songName) {        System.out.println("华仔唱:" + songName);    }    @Override    public void dance(String danceName) {        System.out.println("华仔跳:" + danceName);    }}

3、创建与动态代理类相关的invocationHandler类,invoke的三个参数的定义在方法注释中有体现。

package com.wisely.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** * DES:代理对象 要牛逼的 * Created by Reynole-白 * Date: 2017/8/26 16:30 */public class LiuDeHuaProxy implements InvocationHandler {    private Person pp = new LiuDeHua();    /**     * 代理对象执行的invoke方法,如果想让代理对象做些逻辑操作,可以在invoke中进行编码     * @param proxy   表示代理对象,这个对象才是真正的动态代理对象     * @param method  代理对象当前执行的方法     * @param args    方法参数     * @return     * @throws Throwable     */    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        String methodName = method.getName();        Object obj = args[0];        if(obj instanceof String){            System.out.println("传入的参数是string类型的");        }        //代理对象要做的事情        if("sing".equals(methodName)){            System.out.println("我是华仔的代理对象,找他唱歌先给钱~!!!" +  args[0]);            System.out.println("已收到你的10K RMB,通知华仔……");            pp.sing(args[0].toString());        }else if("dance".equals(methodName)){            System.out.println("我是华仔的代理对象,找他跳舞,先通过我这关!!~~" +  args[0]);            System.out.println("你已通关,通知华仔……");            pp.dance(args[0].toString());        }        return null;    }}

测试类:如果不是多态形式接收代理生成的Object 那么编译不通过

package com.wisely.proxy;import java.lang.reflect.Proxy;/** * DES: 代理测试类 * Created by Reynole-白 * Date: 2017/8/26 16:38 */public class ProxyTestMain {    public static void main(String[] args) {        Person p = new LiuDeHua();//被代理的对象        Person p2 = new JavaFatcher();        /**         * 这里代理对象的生成其实可以写到代理对象中,以匿名内部类的形式         * 这里要注意,代理对象 必须要以多态的形式定义         */        ClassLoader cl = LiuDeHuaProxy.class.getClassLoader();        System.out.println(cl.getParent());        Person personProxy = (Person) Proxy.newProxyInstance(LiuDeHuaProxy.class.getClassLoader(),p.getClass().getInterfaces(),                new LiuDeHuaProxy());        personProxy.sing("冰雨");        personProxy.dance("迪斯科");    }}

运行结果如下:

我是华仔的无参构造器

sun.misc.Launcher$ExtClassLoader@41629346
我是华仔的无参构造器
传入的参数是string类型的
我是华仔的代理对象,找他唱歌先给钱~!!!冰雨
已收到你的10K RMB,通知华仔……
华仔唱:冰雨
传入的参数是string类型的
我是华仔的代理对象,找他跳舞,先通过我这关!!~~迪斯科
你已通关,通知华仔……
华仔跳:迪斯科

Process finished with exit code 0

-------------------------------------------------------------------------------------------------

以上例子,参考了http://blog.csdn.net/pangqiandou/article/details/52964066 这篇博客的例子

其使用场景 就是在大多数框架设计以及 封装设计的时候使用。一般搬砖的像我这样的猿还没有接触到。但如果想看spring这类框架的源码的话尤其是AOP ,是需要了解其动态代理的。

以上,有不对的地方,请个位大大海涵,并友情指正。拜谢!!!

下一步好好研究一下反射。

=============================================================

补充。因为小弟近期在看dubbo的源码。接触到了dubbo 的代理模式。就复习了jdk 的接口类型的动态代理。

感觉之前理解的东西有些出入。特此补充一下:

1、实现InvocationHandler的类  是一个中介类。。负责关联动态生成的代理,并调用被代理对象的方法。

2、代理类在日志输出上都有一些特殊的标记,如:

$Proxy0

0代表第几个。多个会依次累加。

3、真正创建动态代理对象的是Proxy 的newProxyInstance方法 中的Class<?> cl = getProxyClass0(loader, intfs);这句。

4、动态代理对象 在创建时,是存放在jvm缓存中的class文件。反编译后为:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;import proxy.Person;public final class $Proxy0 extends Proxy implements Person{  private static Method m1;  private static Method m2;  private static Method m3;  private static Method m0;    /**  *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白  *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个  *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。  *  *super(paramInvocationHandler),是调用父类Proxy的构造方法。  *父类持有:protected InvocationHandler h;  *Proxy构造方法:  *    protected Proxy(InvocationHandler h) {  *         Objects.requireNonNull(h);  *         this.h = h;  *     }  *  */  public $Proxy0(InvocationHandler paramInvocationHandler)    throws   {    super(paramInvocationHandler);  }    //这个静态块本来是在最后的,我把它拿到前面来,方便描述   static  {    try    {      //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);      m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);      return;    }    catch (NoSuchMethodException localNoSuchMethodException)    {      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());    }    catch (ClassNotFoundException localClassNotFoundException)    {      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());    }  }   /**  *   *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。  *this.h.invoke(this, m3, null);这里简单,明了。  *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,  *再联系到InvacationHandler中的invoke方法。嗯,就是这样。  */  public final void giveMoney()    throws   {    try    {      this.h.invoke(this, m3, null);      return;    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }  //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。}

仔细观察 反编译的 动态代理class 就会明白。为何 实现invocationHandler 类中invoke 方法的三个参数哪里来的对象。怎么用的。

以上补充 参考自:https://www.cnblogs.com/gonjan-blog/p/6685611.html

转载于:https://my.oschina.net/u/2543341/blog/1524330

你可能感兴趣的文章
Linux的任务调度
查看>>
在Android studio中添加jar包方法如下
查看>>
iframe 在ie下面总是弹出新窗口解决方法
查看>>
分享10款漂亮实用的CSS3按钮
查看>>
安装nginx 常见错误及 解决方法
查看>>
Gorun8电子商城
查看>>
在之前链表的基础上改良的链表
查看>>
android编译系统makefile(Android.mk)写法
查看>>
MD5源代码C++
查看>>
Eclipse 添加 Ibator
查看>>
Linux中变量$#,$@,$0,$1,$2,$*,$$,$?的含义
查看>>
Python编程语言
查看>>
十四、转到 linux
查看>>
Got error 241 'Invalid schema
查看>>
ReferenceError: event is not defined
查看>>
男人要内在美,更要外在美
查看>>
为什么要跟别人比?
查看>>
app启动白屏
查看>>
Oracle 提高查询性能(基础)
查看>>
学习知识应该像织网一样去学习——“网状学习法”
查看>>