在《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() { 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[], 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(BiFunctionsubKeyFactory, BiFunction 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
上面是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 ConstructorgetConstructor(Class>... parameterTypes) throws NoSuchMethodException, SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return getConstructor0(parameterTypes, Member.PUBLIC); }
调用了getConstuctor0方法返回一个public的构造方法,
private ConstructorgetConstructor0(Class>[] parameterTypes, int which) throws NoSuchMethodException { Constructor [] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC)); for (Constructor constructor : constructors) { if (arrayContentsEq(parameterTypes, constructor.getParameterTypes())) { return getReflectionFactory().copyConstructor(constructor); } } throw new NoSuchMethodException(getName() + ". " + 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动态代理的简单使用方法及背后的原理,有不当之处欢迎指正,感谢!