java动态代理

作者: SeaRise | 来源:发表于2017-11-20 17:53 被阅读36次

java动态代理用到了java.lang.reflect包的Proxy类和InvocationHandler接口。它们在动态代理中起到的作用如下:

  • Proxy类:提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
    方法摘要:
      //返回指定代理实例的调用处理程序
  - public static InvocationHandler getInvocationHandler(Object proxy) 

    //返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
  - public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)

    //当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。
  - public static boolean isProxyClass(Class<?> cl) 

   //返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
  - public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)

  • InvocationHandler:调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问。生成的动态代理类实际上调用的是invokle方法。
/**
 该方法负责集中处理动态代理类上的所有方法调用。
 第一个参数既是代理类实例,
 第二个参数是被调用的方法对象
 第三个方法是调用参数。
 调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
*/
public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;

接下来放一个使用动态代理的例子。

/*
*被代理对象实现的接口,用JDK来生成代理对象一定要实现一个接口 
*/
public interface PersonDao {
    public void say();
}

/*
*被代理对象
*/
public class PersonDaoImpl implements PersonDao {
    @Override
    public void say() {
        // TODO Auto-generated method stub
        System.out.println("it's time to say");
    }   
}


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/*
*实现自己的InvocationHandler,在生成的代理类中,invoke方法作为回调函数被调用。
*/
public class PersonHandler implements InvocationHandler {
    private Object obj; 
    public PersonHandler(Object obj){
        this.obj=obj;
    }   
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("before");
        Object result = method.invoke(obj, args);
        System.out.println("after");
        return result;
    }
}

/*
*动态代理测试类
*/
public class PersonTest {
    public static void main(String[] args) {
        PersonDao pDao = new PersonDaoImpl();
        PersonHandler handler = new PersonHandler(pDao);        
        PersonDao proxy = (PersonDao)Proxy.newProxyInstance(pDao.getClass().getClassLoader(), pDao.getClass().getInterfaces(), handler);
        proxy.say();
    }
}
image.png

可以看到动态代理的步骤是:

  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  2. 创建被代理类和其实现的接口。
  3. 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,new Class[] { Interface.class }, handler );

通过动态代理生成的动态代理类的字节码是在内存中产生的,并在内存中被类加载器加载。除非指定在硬盘生成二进制文件,否则在硬盘是找不到class文件的。

接下来用下面的这段代码可以生成class文件。

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;
public class PersonTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        writeProxyClassToHardDisk("F:/$Proxy11.class");
    }
    
        //将代理类的字节码写入硬盘
    public static void writeProxyClassToHardDisk(String path) {
        // 获取代理类的字节码  
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", PersonDaoImpl.class.getInterfaces());  
          
        FileOutputStream out = null;  
          
        try {  
            out = new FileOutputStream(path);  
            out.write(classFile);  
            out.flush();  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            try {  
                out.close();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
}

将获得的class文件用反编译工具反编译后,可以获得下面的动态代理类的代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.PersonDao;

public final class $Proxy11
  extends Proxy
  implements PersonDao
{
  private static Method m3;
  private static Method m1;
  private static Method m0;
  private static Method m2;
  
  public $Proxy11(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }
  
  public final void say()
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String toString()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  static
  {
    try
    {
      m3 = Class.forName("proxy.PersonDao").getMethod("say", new Class[0]);
      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]);
      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());
    }
  }
}

从上面反编译后的动态代理类的代码可以得知:

  • Proxy是动态代理类的父类,所以动态代理的被代理对象必须是实现一个接口,因为java是单继承多实现接口的机制。

  • 除了say()方法被代理外,可以发现equals,hashCode,toString这三个方法同样也被代理了,所以调用这三个方法同样会和say方法一样,在方法执行前后打印"before"和"after"。

  • 通过对代码的分析,我们可以得出下面的关系:


    O79~LEJ@`5W9FYEG}`WK}Z6.png

接下来尝试跟踪一下源码,由于我水平有限,有些地方不会细究下去,而是跳过。

Proxy类

 /** parameter types of a proxy class constructor 
** 生成的代理类构造函数的参数的类。
*/
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    /**
     * a cache of proxy classes
     *动态代理类的缓存
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    /**
     * the invocation handler for this proxy instance.
     * 关联的调用处理器引用
     * @serial
     */
    protected InvocationHandler h;

从这里可以看到Proxy的几个成员,特别是InvocationHandler h,在生成的动态代理类中用到的h就是从父类Proxy中继承而来的。

Proxy 静态方法 newProxyInstance

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

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }

不要管那些安全检查和异常处理的代码,只关注代码的主要逻辑,我们可以发现newProxyInstance方法主要做了一下工作:

  • 调用interfaces.clone(),获取被代理类的接口的副本

  • Class<?> cl = getProxyClass0(loader, intfs);获取生成的动态代理类的Class对象

  • final Constructor<?> cons = cl.getConstructor(constructorParams);获取动态代理类的构造函数

  • return newInstance(cons, ih);根据构造函数和InvocationHandler调用处理器来生成动态代理类的实例,并返回给调用程序。跟踪这个方法,我们可以看到除去异常处理的代码,这个方法里的代码只有一行:
    return cons.newInstance(new Object[] {h} );
    用构造函数生成动态代理类的实例。

这里面最重要的是Class<?> cl = getProxyClass0(loader, intfs);接下来看一下这个方法的源码。

getProxyClass0

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

可以看到,在检查接口数量是否大于65535后,就立刻从proxyClassCache中获取被动态代理类的Class对象,proxyClassCache就是之前提到的Proxy的成员,是动态代理类的缓存。

上面的注释说到:

// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory

意思是说动态代理类的Class对象已经存在,就从proxyClassCache中取,如果不存在,就通过ProxyClassFactory类创建,ProxyClassFactory是Proxy的一个内部类,接下来跟踪一下ProxyClassFactory。

ProxyClassFactory

// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";

// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();

生成的代理类的名字是由上面的两个成员来决定。可知动态代理类的名字应该是:$ProxyN,N是逐一递增的数字,代表Proxy被第N次动态生成的代理类。

ProxyClassFactory类只有一个方法:
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces)
这个方法用于创建动态代理类。

这个方法前面的一大部分都是在验证

  • 参数里的类加载器是否和被代理类的接口的类加载器的一致。
  • 参数里的Class对象是不是接口的Class对象
  • 接口有没有重复
  • 非public接口是不是都在一个包里,是的话动态代理类也在那个包里,
    如果没有非public接口,动态代理类就在com.sun.proxy包里。
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

后面的一部分是为动态代理类分配名字,就是利用之前提到的两个成员。

long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;

最后就是生成动态代理类的Class对象。

           byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }

去除异常处理的代码,关键的代码就只有两行:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
  • 第一行在最上面在硬盘生成Class文件的代码里用过,作用是生成二进制的字节码序列。
  • 第二行就是通过字节码序列,类加载器和类名生成动态代理类。
    这一行代码是一个native方法,到这里就无法跟踪下去了。

简而言之,当已经存在所需的动态代理类时,就会返回一个先前已经创建并缓存了的代理类对象,只有不存在时,才会调用ProxyClassFactory类的apply方法,通过类加载器和接口创建动态加载类。
从apply方法的参数只有类加载器和接口来看,动态代理类重复的条件应该是类加载器和接口一致,与InvocationHandler调用处理器无关。

大致上jdk动态代理的过程就是这样了。

相关文章

  • Java 动态代理

    java的动态代理机制详解 JDK动态代理详解 Java核心技术点之动态代理

  • JDK动态代理详解

    JDK动态代理详解 java动态代理类 Java动态代理类位于java.lang.reflect包下,一般主要涉及...

  • Java动态代理从入门到原理再到实战

    目录 前言 什么是动态代理,和静态代理有什么区别 Java动态代理的简单使用 Java动态代理的原理解读 动态代理...

  • java反射和动态代理

    java动态代理Proxy.newProxyInstance 详解java代理机制(静态代理、动态代理)以及使用场景

  • 保存java 动态代理生成的字节码文件

    保存java 动态代理生成的字节码文件 在Java中,常用的动态代理技术有JDK的动态代理和cglib动态代理,不...

  • java动态代理(JDK和cglib)(转载自http://ww

    java动态代理(JDK和cglib) JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是...

  • java基础巩固笔记(4)-代理

    标签: java Contents java基础巩固笔记(4)-代理概念动态代理创建动态类动态代理的工作原理面向切...

  • java 动态代理

    动态代理动态代理可以让我们在运行时动态生成代理类,解耦程度更高。Java 动态代理的实现主要借助于 java.la...

  • java随笔(十一)

    java动态代理源码分析,总结。java动态代理实现步骤: 通过阅读源码发现,动态生成代理对象$Proxy0,该对...

  • Java基础:反射

    反射注解动态代理相关阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 ...

网友评论

    本文标题:java动态代理

    本文链接:https://www.haomeiwen.com/subject/qytbvxtx.html