JDK动态代理

在《springAOP之代理模式》中说了代理模式,包含静态代理和动态代理,在动态代理模式中又分为JDK动态代理和CGlib动态代理,今天重点来看JDK动态代理。

一、概述

说到JDK动态代理就必须想到JDK动态代理要求有一个统一的接口,那为什么要有接口,下面会说到,下面看我的接口类,

package cn.com.jdk.proxy;

public interface Subject {

    void sayHello(String a);
}

接口类很简单就是一个简单的方法定义。下面看实际的接口的实现类SubjectImpl,

package cn.com.jdk.proxy;

public class SubjectImpl implements Subject {

    @Override
    public void sayHello(String a) {
        // TODO Auto-generated method stub

        System.out.println("hello:"+a);
    }

}

实现类简单的事项了Subject接口,进行了打印操作。下面看代理类

package cn.com.jdk.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JDKProxy implements InvocationHandler {
    private SubjectImpl si;
    //此属性不用管
    private String a;
/**
 * proxy  JDK动态生成的代理类的实例
 * method 目标方法的Method对象     Class.forName("cn.com.jdk.proxy.Subject").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
 * args   目标方法的参数                       new Object[] { paramString }
 */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("before");
        //使用反射的放式调用si(被代理类)目标方法
        Object o=method.invoke(si, args);
        System.out.println("after");
        return o;
    }
    public JDKProxy(SubjectImpl si,String a) {
        this.si=si;
        this.a=a;
    }

}

上面是代理类的实现,在代理类中含义被代理类的一个引用,且提供了响应的构造方法。下面具体的使用,

package cn.com.jdk.proxy;

import java.lang.reflect.Proxy;

public class ProxyTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //进行此项设置,可以在项目的com/sun/proxy目录下找到JDK动态生成的代理类的字节码文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        SubjectImpl si=new SubjectImpl();
        
        Subject subject=(Subject)Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), new JDKProxy(si,"111"));
        subject.sayHello("tom");

    }

}

上面是使用的代码,通过Proxy类的newProxyInstance方法获得一个Subject的实例,调用sayHello方法,下面看执行结果

before
hello:tom
after

可以看到执行了sayHello方法,且打印了before和after,这不正是代理类中invoke方法的执行吗,看下面

很神奇的一件事,我们不光调用了sayHello方法,实现了打印,而且在加入了自己的打印方法,这不正是AOP的增强功能吗。这一切是怎么发生的那,下面细细说来。

二、详述

上面,我们又复习了JDK动态代理的内容,以及演示了如何使用JDK动态代理,下面我们要看这是怎么实现的,先从测试的下面这段代码说起,也是最重要的代码,JDK动态代理的精华都在这句代码里,

Subject subject=(Subject)Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), new JDKProxy(si,"111"));

这句代码是调用了Proxy类的newProxyInstance方法,此方法的入参如下,

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

 一共三个参数,一个是ClassLoader,这里传入的是被代理对象的类加载器;一个是Class,这里传入的是被代理对象所实现的接口;一个是InvocationHandler,这里传入的是代理类,代理类实现了InvocationHandler接口。

 1、newProxyInstance方法

下面看newProxyInstance方法的定义,

@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.
         */
          //1、使用代理类的类加载器和其所实现的接口,动态生成代理类
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //2、返回JDK生成的代理类的构造方法,该构造方法的参数为
            //  InvocationHandler
            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;
                    }
                });
            }
//3、返回该构造方法的一个实例,也就是使用InvocationHandler为参数的构造方法利用反射的机制返回一个实例。
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.1、getProxyClass0(loader, intfs)方法

该方法便是上面的第一步,这一步的作用是JDK返回一个代理类的实例,方法上的注释如下,

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

注释直译过来是查找或者生成指定的代理类,这里有两层意思,一个是查找,第二个是生成,由此可以想到这个方法中应该有缓存,下面看方法的具体定义,

/**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    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便抛异常,接口的数量大于65535的可能性不大。最后调用了proxyClassCache的get方法,首先看proxyClassCache,从字面上理解是代理类的缓存,看其定义,

/**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

是一个WeakCache对象实例,看下该构造方法,

/**
     * Construct an instance of {@code WeakCache}
     *
     * @param subKeyFactory a function mapping a pair of
     *                      {@code (key, parameter) -> sub-key}
     * @param valueFactory  a function mapping a pair of
     *                      {@code (key, parameter) -> value}
     * @throws NullPointerException if {@code subKeyFactory} or
     *                              {@code valueFactory} is null.
     */
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }

看了该类的构造方法后,回到proxyClassCache.get(loader, interfaces)方法的调用,我们已经知道proxyClassCache是WeakCache的一个实例,那么get方法如下,

 /**
     * Look-up the value through the cache. This always evaluates the
     * {@code subKeyFactory} function and optionally evaluates
     * {@code valueFactory} function if there is no entry in the cache for given
     * pair of (key, subKey) or the entry has already been cleared.
     *
     * @param key       possibly null key
     * @param parameter parameter used together with key to create sub-key and
     *                  value (should not be null)
     * @return the cached value (never null)
     * @throws NullPointerException if {@code parameter} passed in or
     *                              {@code sub-key} calculated by
     *                              {@code subKeyFactory} or {@code value}
     *                              calculated by {@code valueFactory} is null.
     */
    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

 上面是WeakCache的get方法,这个方法暂时不作说明,后面会详细介绍WeakCache类,请参见《JDK动态代理之WeakCache 》。这里只需记住该get方法会返回一个代理类的实例即可。那么此代理类是如何定义的那?

1.1.1、$Proxy0.class代理类

这个代理类是JDK动态生成的,其命名规则为以“$”开头+Proxy+“从0开始的序列”。上面在测试的时候,我们加入了下面这行代码,

//进行此项设置,可以在项目的com/sun/proxy目录下找到JDK动态生成的代理类的字节码文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

注释中写到可以生成代理类的字节码文件,下面是使用反编译工具过来的java代码,

package com.sun.proxy;

import cn.com.jdk.proxy.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy
  implements Subject
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
 //参数为InvocationHandler的构造方法
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
   //调用父类Proxy的构造方法,在父类的构造方法中会初始化h属性
    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);
  }
//实现的Subject的sayHello方法
  public final void sayHello(String paramString)
    throws 
  {
    try
    {
      //调用h的invoke方法,这里的h指的是实现了InvocationHandler的类
      //调用其中的invoke方法,在本例中是调用JDKProxy类中的invoke方
      //
      this.h.invoke(this, m3, new Object[] { paramString });
      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);
  }

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

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("cn.com.jdk.proxy.Subject").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", 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());
  }
}

上面是反编译过来的JDK生成的代理类的代码,包含了一个使用InvocationHandler作为参数的构造方法,以及实现了Subject接口的sayHello方法。上面注释中写到该构造方法调用了其父类Proxy的构造方法,下面看其父类Proxy的构造方法,

protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

把InvocationHandler的值赋给了h,h的定义如下,

protected InvocationHandler h;

那么在生成的代理类中自然会继承该属性,所以在代理类中的sayHello中使用下面的方法调用,

public final void sayHello(String paramString)
    throws 
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { paramString });
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

上面的this.h便是其父类的h属性。在上面的this.h.invoke中的m3是怎么来的那,看下面,

 static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("cn.com.jdk.proxy.Subject").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", 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());
  }

在该类的静态代码块中给出了4个属性。

1.2、getConstructor(constructorParams)方法

在上面的getProxyClass0方法中我们知道该方法会返回一个JDK生成代理类的Class对象,此类的定义便是上面的$Proxy0.class类。其定义在上面已经分析过。getConstructor方法要返回一个以constructorParams为参数的构造方法,

@CallerSensitive
    public Constructor<T> getConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return getConstructor0(parameterTypes, Member.PUBLIC);
    }

调用了getConstuctor0方法返回一个public的构造方法,

private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                        int which) throws NoSuchMethodException
    {
        Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
        for (Constructor<T> constructor : constructors) {
            if (arrayContentsEq(parameterTypes,
                                constructor.getParameterTypes())) {
                return getReflectionFactory().copyConstructor(constructor);
            }
        }
        throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
    }

上面的方法会返回一个public的构造方法。

回到最初的调用,我们看getConstructor方法的参数是constructorParams,此属性定义如下,

/** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

是一个Class数组,其类型为InvocationHandler。这样便可以知道是通过代理类的Class对象返回其构造方法cons。有了构造方法下面便是通过构造方法生成实例。

1.3、cons.newInstance(new Object[]{h})方法

此方法便是通过构造方法返回一个代理类的实例。

上面分析了Proxy的newProxyInstance方法,此方法最终会返回一个代理类的实例,会经过下面几个步骤,

从上面的步骤,我们知道在获得代理类的构造方法时,是获得其参数为InvocationHandler的构造方法,所以肯定要实现InvocationHandler接口,在本例中便是JDKProxy类,这个类实现了这个接口。值开篇我们讲到JDK动态代理必须要有统一的接口,从上面的步骤中我们知道在生成代理类的Class对象时使用了两个参数,一个ClassLoader,另一个是接口,这里就是为什么要有统一的接口,因为在生成代理类的Class对象中需要接口,所以被代理类必须要有一个接口。

2、方法调用

这里的方法调用,便是对应使用方法中的下面这行代码,

subject.sayHello("tom");

在上面的分析中获得了一个代理类的实例,即下面这行代码,

Subject subject=(Subject)Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), new JDKProxy(si,"111"));

通过使用被代理类的类加载器、被代理类所实现的接口、实现了InvocationHandler接口的类的实例三个参数,返回了一个代理类的实例。上面已经详细分析过。此代理类的实例继承了Proxy,实现了Subject接口。其sayHello方法如下,

public final void sayHello(String paramString)
    throws 
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { paramString });
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

上面已经分析过,this.h是InvocationHandler的实例,这里便是new JDKProxy(si,"111"),m3是m3 = Class.forName("cn.com.jdk.proxy.Subject").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });下面看JDKProxy中的invoke方法,

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("before");
        //使用反射的放式调用目标方法
        Object o=method.invoke(si, args);
        System.out.println("after");
        return o;
    }

此方法的三个参数分别为代理类的实例、Method对象(sayHello),调用sayHello时的参数,所以要调用被代理类的sayHello方法,需要这样写:method.invoke(si,args),即调用被代理类(SubjectImpl)的sayHello方法,参数为args(tom)。下面是一个简单的方法调用过程,

三、总结

本文分析了JDK动态代理的简单使用方法及背后的原理,有不当之处欢迎指正,感谢!

原文地址:https://www.cnblogs.com/teach/p/13143430.html