在《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動態代理的簡單使用方法及背後的原理,有不當之處歡迎指正,感謝!