java 动态代理初步理解

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

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

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

下面在说下Proxy,该对象主要负责建立代理,建立代理对象的源码以下:数组

@CallerSensitive
public 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<Void>() {
                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);
    }
}

三个参数分别是:缓存

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

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

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

三、代理类对象相关联的invocationHandler,是invocationHandler对象测试

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

步骤以下:

一、建立一个实体接口

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

}

二、建立其实现

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

三、建立与动态代理类相关的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 的接口类型的动态代理。

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

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

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

$Proxy0

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

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

四、动态代理对象 在建立时,是存放在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

相关文章
相关标签/搜索