动态代理之JDK Proxy浅析

反射:运行时动态实例化任何一个类

看此文章前至少对JAVA反射有一定了解...

Jdk动态代理的实现就是使用了反射机制,关键代码在Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)。可以看到该方法中的最后一个参数InvocationHandler,这是动态代理实现的另一个关键点。

直接上代码,接口、目标类、调用处理类:

/**
 * @author longe
 *    目标类实现的接口
 */
public interface Parent {
    void doSomething();
}

/**
 * @author longe
 *    目标类
 */
public class Son implements Parent{
    @Override
    public void doSomething() {
        System.out.println(" son is doing something ! ");
    }
}

/**
 * @author longe 动态代理
 *         不需要为真实主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,
 *         则真实主题和代理类都要修改,不利于系统维护; 使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。
 *         JDK动态代理要求, 被代理的必须是个接口
 */
public class JDKDynamicProxy implements InvocationHandler {

    private Object obj;

    public JDKDynamicProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy invoke...");
        return method.invoke(this.obj, args);
    }

    /**
     * 输出代理类字节码到本地class文件
     */
    private static void generateClassFile(String proxyName, Class clazz) {
        byte[] bts = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
        try {
            FileOutputStream fos = new FileOutputStream(new File("F:\\" + proxyName + ".class"));
            fos.write(bts);
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Parent son = new Son();
        // 只要实现了Parent接口的就都可以代理
        Parent parent = (Parent) Proxy.newProxyInstance(Parent.class.getClassLoader(), son.getClass().getInterfaces(),
                new JDKDynamicProxy(son));
        generateClassFile("Son$proxy0", parent.getClass());
        parent.doSomething();
    }
}

简单的动态代理实现,主要看JDKDynamicProxy类的实现,它实现了InvocationHandler接口,说明它是一个调用处理器,用作Proxy.newProxyInstance创建代理实例时的形参。

通过Proxy.newProxyInstance返回代理实例后就可以直接调用了,这里我通过ProxyGenerator.generateProxyClass获取到代理类的字节码,并把它输出到本地文件Son$proxy0.class中

public final class Son$proxy0 extends Proxy
  implements Parent
{
  private static Method m1;
  private static Method m0;
  private static Method m3;
  private static Method m2;

  public Son$proxy0(InvocationHandler paramInvocationHandler)
    throws
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void doSomething()
    throws
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m3 = Class.forName("practices.model.proxy.Parent").getMethod("doSomething", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

Son$proxy0.class

通过反编译后可以看到,生成的代理类默认继承了Proxy,同时实现了我们指定的接口。在实现的doSomething()方法中可以看到this.h.invoke(this, m3, null);即调用了我们指定的调用处理器中的invoke方法,也就是JDKDynamicProxy中的invoke方法。

到这里,终于明白invoke是从哪里调用的了。那么为什么JDK动态代理的类必须实现接口呢?呵呵,因为生成的代理实例默认继承了Proxy类啊,且JAVA是不支持多继承的,SO....

那么JDK动态代理的实现过程粗略的理清了

下面简单的介绍一下这两个关键点(源码来自JDK1.7)

  • InvocationHandler

调用处理器接口,Proxy中的成员变量,代理类继承了Proxy,所以可以通过调用它的invoke方法来实现目标方法的调用

  • Proxy.newProxyInstance

  解析一下Proxy:

Proxy类的主要的属性如下,在创建代理实例和调用时使用

public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;

    /** 代理类构造器的参数类型 */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    /**
     * 代理类Class对象缓存
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    /**
     * 当前代理实例的调用处理器
     */
    protected InvocationHandler h;

    /**
     * 禁用的构造器.
     */
    private Proxy() {
    }

    /**
     * 通过动态代理子类对象的构造器创造代理实例时指定了它的调用处理器(子类构造器中调用此构造器)
     * @param   当前代理实例的调用处理器
     */
    protected Proxy(InvocationHandler h) {
        doNewInstanceCheck();
        this.h = h;
    }  ......

Proxy.newProxyInstance方法用于返回一个指定的代理实例对象

    /**
     * loader:指定代理类的类加载器
     * interfaces:代理类实现的接口列表
     * h:代理类方法调用的调用处理器
     * 返回一个持有代理类处理器的代理实例,它由指定的类加载器定义,并实现了指定的接口(此代理类继承Proxy,由此可知为何目标类必须实现接口,因为java中不支持多继承,下面详解)
     */
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        /*
         * 从缓存中查找或生成指定的代理类Class对象(此处就是生成代理类字节码的调用处,底层生成字节码调用了ProxyGenerator.generateProxyClass方法)
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 调用代理类Class对象指定参数为调用处理器的构造函数,
         */
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                /*
                 * 实例化此代理类,参数为调用处理器,相当于调用了 Proxy(InvocationHandler h)构造函数(因为代理类继承Proxy类)
                 */
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

OK,结合前面的例子理解起来就没这么费劲拉...

总结

创建动态代理步骤:

首先通过实现InvocationHandler接口创建自己的调用处理器

然后使用Proxy的newProxyInstance创建代理类实例对象

不足

JDK动态代理明显的一个不足就是无法摆脱仅支持接口代理,它的设计注定了这个遗憾。因为代理类继承了一个共同的基类Proxy,且由于JAVA单继承的特性,所以在生成字节码时代理实例继承了Proxy,实现了指定接口(Cglib弥补了这个缺憾)

时间: 2024-10-30 23:31:02

动态代理之JDK Proxy浅析的相关文章

java动态代理(JDK和cglib)

转自:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务. 按照代理的创建时期,

《转》java动态代理(JDK和cglib)

该文章转自:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务. 按照代理的创建

《转》JAVA动态代理(JDK和CGLIB)

该文章转自:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务. 按照代理的创建

(转)java动态代理(JDK和cglib)

博文转自http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务. 按照代理的创建时期

代理模式-JDK Proxy(Java实现)

代理模式-JDK Proxy 使用JDK支持的代理模式, 动态代理 场景如下: 本文例子代理了ArrayList, 在ArrayList每次操作时, 在操作之前和之后都进行一些额外的操作. ArrayListProxy类 这里是代理的实现. import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ArrayListProxy implements InvocationHan

JDK动态代理[2]----JDK动态代理的底层实现之Proxy源码分析

在上一篇里为大家简单介绍了什么是代理模式?为什么要使用代理模式?并用例子演示了一下静态代理和动态代理的实现,分析了静态代理和动态代理各自的优缺点.在这一篇中笔者打算深入源码为大家剖析JDK动态代理实现的机制,建议读者阅读本篇前可先阅读一下笔者上一篇关于代理模式的介绍<JDK动态代理[1]----代理模式实现方式的概要介绍> 上一篇动态代理的测试类中使用了Proxy类的静态方法newProxyInstance方法去生成一个代理类,这个静态方法接收三个参数,分别是目标类的类加载器,目标类实现的接口

探索Mybatis之JDK动态代理:探究Proxy.newProxyInstance()生成的代理类解析

Mybatis的Mapper接口UserMapper 1 package com.safin.Mapper; 2 3 import com.safin.Pojo.User; 4 5 import java.util.List; 6 7 public interface UserMapper { 8 int insertUser(User user); 9 User getUser(Long id); 10 List<User> findUser(String userName); 11 } 我

【设计模式】动态代理 &amp;&amp; 模拟JDK动态代理

真正理解动态代理需要明白回答以下问题: 什么叫动态代理?怎么产生? 动态代理的作用?可配置的事务,权限控制,日志等等....只有你想不到,没有动态代理做不到. 下面来回答以上3个问题: 先说下静态代理: 方法:创建代理类,代理类包含被代理对象的方法并在被代理方法的前后加添加的方法. 创建代理类可以用继承接口或者聚合(implements)被代理对象的接口来实现,然后传入被代理对象的实例.其中聚合并继承好,使用继承的时候如果代理类需要嵌套代理类或者创建不同的代理类,需要创建不同的代理类.造成类泛滥

动态代理(JDK实现)

JDK动态代理,针对目标对象的接口进行代理 ,动态生成接口的实现类 !(必须有接口) public class ProxyDemo { //通过方法的返回值得到代理对象            方法参数是要增强的对象    public Object getProxyObject(final Object target) {                Object proxyObj = Proxy.newProxyInstance(   //调用Proxy类中的静态方法,创建代理对象