Spring AOP(二) 修飾者模式和JDK Proxy
在上邊一篇 文章 中我們介紹了Spring AOP的基本概念,今天我們就來學習一下與AOP實現相關的修飾者模式和Java Proxy相關的原理,為之後原始碼分析打下基礎。
修飾者模式
Java設計模式中的修飾者模式能動態地給目標物件增加額外的職責(Responsibility)。它使用組合(object composition),即將目標物件作為修飾者物件(代理)的成員變數,由修飾者物件決定呼叫目標物件的時機和呼叫前後所要增強的行為。
裝飾模式包含如下組成部分:
- Component: 抽象構件,也就是目標物件所實現的介面,有operation函式
- ConcreteComponent: 具體構件,也就是目標物件的類
- Decorator: 抽象裝飾類,也實現了抽象構件介面,也就是目標類和裝飾類都實現了相同的介面
- ConcreteDecorator: 具體裝飾類,其中addBeavior函式就是增強的行為,裝飾類可以自己決定addBeavior函式和目標物件函式operation函式的呼叫時機。
修飾者模式呼叫的時序圖如下圖所示。程式首先建立目標物件,然後建立修飾者物件,並將目標物件傳入作為其成員變數。當程式呼叫修飾者物件的operation函式時,修飾者物件會先呼叫目標物件的operation函式,然後再呼叫自己的addBehavior函式。這就是類似於AOP的後置增強器,在目標物件的行為之後新增新的行為。
Spring AOP的實現原理和修飾者模式類似。在上一篇文章中說到AOP的動態代理有兩種實現方式,分別是JDK Proxy和cglib。
如下圖所示,JDK Proxy的類結構和上文中修飾者的類圖結構類似,都是代理物件和目標物件都實現相同的介面,代理物件持有目標物件和切面物件,並且決定目標函式和切面增強函式的呼叫時機。
而cglib的實現略有不同,它沒有實現實現相同介面,而是代理物件繼承目標物件類。
本文後續就講解一下JDK Proxy的相關原始碼分析。
JDK Proxy
JDK提供了Proxy類來實現動態代理的,可通過它的newProxyInstance函式來獲得代理物件。JDK還提供了InvocationHandler類,代理物件的函式被呼叫時,會呼叫它的invoke函式,程式設計師可以在其中實現所需的邏輯。
JDK Proxy的基本語法如下所示。先構造一個 InvocationHandler
的實現類,然後呼叫 Proxy
的 newProxyInstance
函式生成代理物件,傳入類載入器,目標物件的介面和自定義的 InvocationHandler
例項。
public class CustomInvocationHandler implements InvocationHandler { private Object target; public CustomInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before invocation"); Object retVal = method.invoke(target, args); System.out.println("After invocation"); return retVal; } } CustomInvocationHandler customInvocationHandler = new CustomInvocationHandler( helloWord); //通過Proxy.newProxyInstance生成代理物件 ProxyTest proxy = (ProxyTest) Proxy.newProxyInstance( ProxyTest.class.getClassLoader(), proxyObj.getClass().getInterfaces(), customInvocationHandler);
生成代理物件
我們首先來看一下 Proxy
的 newProxyInstance
函式。 newProxyInstance
函式的邏輯大致如下:
InvocationHandler
具體原始碼如下所示。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } // 1 動態生成代理物件的類 Class<?> cl = getProxyClass0(loader, intfs); // ... 程式碼省略,下邊程式碼其實是在try catch中的 if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 2 獲取代理類的建構函式 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}); }
getProxyClass0
函式的原始碼如下所示,通過代理類快取獲取代理類資訊,如果不存在則會生成代理類。
// 生成代理類 private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 如果已經有Proxy類的快取則直接返回,否則要進行建立 return proxyClassCache.get(loader, interfaces); }
生成代理類
JDK Proxy通過 ProxyClassFactory
生成代理類。其 apply
函式大致邏輯如下:
- 校驗介面是否符合規範
- 生成代理類的名稱和包名
- 生成代理類位元組碼
- 根據位元組碼生成代理類Class
// 生成代理類的工廠類 private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // 所有代理類名的字首 private static final String proxyClassNamePrefix = "$Proxy"; // 生成唯一類名的原子Long物件 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { // 通過loader找到介面對應的類資訊。 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"); } // 判斷找出來的類確實是一個介面 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } // 判斷介面是否重複 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } // 代理類的包路徑 String proxyPkg = null; int accessFlags = Modifier.PUBLIC | Modifier.FINAL; // 記錄非公開的代理介面,以便於生成的代理類和原來的類在同一個路徑下。 for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; 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"); } } } // 如果沒有非公開的Proxy介面,使用com.sun.proxy報名 if (proxyPkg == null) { proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } long num = nextUniqueNumber.getAndIncrement(); // 預設情況下,代理類的完全限定名為:com.sun.proxy.$Proxy0,$Proxy1……依次遞增 String proxyName = proxyPkg + proxyClassNamePrefix + num; // 生成代理類位元組碼 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { // 根據位元組碼返回相應的Class例項 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } }
其中關於位元組碼生成的部分邏輯我們就暫時不深入介紹了,感興趣的同學可以自行研究。
$Proxy反編譯
我們來看一下生成的代理類的反編譯程式碼。代理類實現了 Object
的基礎函式,比如 toString
、 hasCode
和 equals
,也實現了目標介面中定義的函式,比如說 ProxyTest
介面的 test
函式。
$Proxy
中函式的實現都是直接呼叫了 InvocationHandler
的 invoke
函式。
public final class $Proxy0 extends Proxy implements ProxyTest // 會實現目標介面,但是由於集成了Proxy,所以無法再整合其他類 { private static Method m1; private static Method m0; private static Method m3; private static Method m2; // 建構函式要傳入一個InvocationHandler物件 public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } // equal函式 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); } // test函式,也就是ProxyTest介面中定義的函式 public final void test(String paramString) throws { try { // 呼叫InvocationHandler的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); } // 獲取各個函式的Method物件 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("com.proxy.test2.HelloTest").getMethod("say", new Class[] { Class.forName("java.lang.String") }); 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()); } }
後記
下一篇文章就是AOP的原始碼分析了,希望大家繼續關注。