Spring溫故而知新 – AOP代理
AOP的概念
AOP:Aspect-Oriented Programming(面向切面編程),維基百科的解釋如下:Aspect是一種新的模塊化機制,用來描述分散在對象、類或者函數中的橫切關註點,從關註點中分離出橫切關註點是面向切面的程序設計的核心概念。分離關註點使解決特定領域問題的代碼從業務邏輯中獨立出來,業務邏輯的代碼中不在含有針對特定領域問題的代碼的調用,業務邏輯同特定領域問題的關系通過切面來封裝、維護,這樣原本分散在整個應用程序中的變動就可以很好地管理起來。從AOP的角度,應用可以分為橫切關註點和業務邏輯代碼,實際開發中,這些橫切關註點往往會直接嵌入到業務邏輯代碼中,面向切面編程就是要解決把橫切關註點與業務邏輯相分離
實現方式:
Spring默認使用 JDK 動態代理作為AOP的代理,缺陷是目標類的類必須實現接口,否則不能使用JDK動態代理。如果需要代理的是類而不是接口,那麽Spring會默認使用CGLIB代理,關於兩者的區別:jdk動態代理是通過java的反射機制來實現的,目標類必須要實現接口,cglib是針對類來實現代理的,他的原理是動態的為指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。
JDK動態代理
Jdk動態代理是在程序運行過程中,根據目標類實現的接口來動態生成代理類的class文件,使用主要涉及兩個類:
InvocationHandler接口: 它提供了一個invoke(Object obj,Method method, Object[] args)方法供實現者提供相應的代理邏輯的實現。可以對實際的實現進行一些特殊的處理其中參數
Object obj :被代理的目標類
Method method: 需要執行的目標類的方法
Object[] args :目標方法的參數
Proxy類:提供一個方法newProxyInstance (ClassLoader loader, Class[] interfaces, InvocationHandler h)來獲得動態代理類
示例代碼:
public interface OrderService { public void createOrder(); }
public class OrderServiceImpl implementsOrderService { @Override public void createOrder() { System.out.println("creating order"); } }
public class OrderLogger { public void beforeCreateOrder(){ System.out.println("before create order"); } public void afterCreateOrder(){ System.out.println("after create order"); } }
package com.sl.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ServiceProxy implements InvocationHandler { private Object targetClass; private OrderLogger orderLogger; public ServiceProxy(Object targetClass,OrderLogger orderLogger) { this.targetClass = targetClass; this.orderLogger = orderLogger; } //獲取代理 public Object GetDynamicProxy() { return Proxy.newProxyInstance(targetClass.getClass().getClassLoader(), //通過這個ClassLoader生成代理對象 targetClass.getClass().getInterfaces(),//代理類已實現的接口 this); //動態代理調用方法是關聯的InvocationHandler,最終通過此InvocationHandler的invoke方法執行真正的方法 } //實現相應的代理邏輯 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { this.orderLogger.beforeCreateOrder(); Object result= method.invoke(targetClass, args); this.orderLogger.afterCreateOrder(); return result; } }
測試類:
package com.sl.aop; import org.junit.Test; public class AopTest { @Test public void Testdynamicproxy() { OrderServiceImpl serviceImpl = new OrderServiceImpl(); OrderLogger logger = new OrderLogger(); OrderService service = (OrderService) new ServiceProxy(serviceImpl, logger).GetDynamicProxy(); service.createOrder(); } }
運行結果:
到這個其實還是有點困惑,Proxy.newProxyInstance()這個返回的是什麽? Invoke方法在哪裏調用的?我們看一下JDK源碼:看看DK動態代理的過程是什麽樣的:
根據源碼內部的函數調用Proxy.newProxyInstance()->Proxy.getProxyClass0()->WeakCache.get() ,先定位到
WeakCache.class:
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); } } } }
可以看到函數return value; 而 V value = supplier.get(); 繼續往下讀可以發現 supper=factory,實際上是一個Factory對象,那麽繼續查看Factory.get()方法
public synchronized V get() { // serialize access // re-check Supplier<V> supplier = valuesMap.get(subKey); if (supplier != this) { // something changed while we were waiting: // might be that we were replaced by a CacheValue // or were removed because of failure -> // return null to signal WeakCache.get() to retry // the loop return null; } // else still us (supplier == this) // create new value V value = null; try { value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { // remove us on failure valuesMap.remove(subKey, this); } } // the only path to reach here is with non-null value assert value != null; // wrap value with CacheValue (WeakReference) CacheValue<V> cacheValue = new CacheValue<>(value); // try replacing us with CacheValue (this should always succeed) if (valuesMap.replace(subKey, this, cacheValue)) { // put also in reverseMap reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } // successfully replaced us with new CacheValue -> return the value // wrapped by it return value; }
Return value;那麽直接查看賦值語句:value = Objects.requireNonNull(valueFactory.apply(key, parameter)); valueFactory又什麽鬼?
public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) { this.subKeyFactory = Objects.requireNonNull(subKeyFactory); this.valueFactory = Objects.requireNonNull(valueFactory); } private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
可以知道valueFactory是ProxyClassFactory類型對象,直接查看ProxyClassFactory. Apply()方法
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { 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 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * 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)) { 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"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); 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, accessFlags); return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
調用ProxyGenerator.generateProxyClass最終動態生成一個代理類,但是似乎並未找到何處調用了invoke方法;參考CSDN: https://blog.csdn.net/jiankunking/article/details/52143504這邊文章,嘗試將這個動態生成的二進制字節碼輸出到本地,並反編譯出來一看究竟,測試代碼如下:
public class AopTest { @Test public void Testdynamicproxy() { OrderServiceImpl serviceImpl = new OrderServiceImpl(); OrderLogger logger = new OrderLogger(); OrderService service = (OrderService) new ServiceProxy(serviceImpl, logger).GetDynamicProxy(); service.createOrder(); //輸出動態代理類字節碼 createProxyClassFile(); } private static void createProxyClassFile(){ String name = "ProxyObject"; byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{OrderService.class}); FileOutputStream out =null; try { out = new FileOutputStream(name+".class"); System.out.println((new File("hello")).getAbsolutePath()); out.write(data); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if(null!=out) try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }
使用java Decompiler工具將這個二進制class文件反編譯查看:
具體動態代理類ProxyObject.java:
import com.sl.aop.OrderService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class ProxyObject extends Proxy implements OrderService { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public ProxyObject(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } 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 String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void createOrder() { try { this.h.invoke(this, m3, null); return; } 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); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.sl.aop.OrderService").getMethod("createOrder", 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()); } }
終於看到關於invoke的部分了:
public final void createOrder() { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }
實際上動態代理類繼承自Proxy,並且實現了目標類繼承的接口,在createOrder方法中調用了invoke方法,實現了切面邏輯的植入,這裏也回答了一個問題,為什麽JDK動態代理的目標類必須是實現接口的,因為代理類其實是針對接口代理,而不是針對類來代理的,動態代理類自己繼承自Proxy,Java也不允許多重繼承。動態代理類和目標類其實是各自實現了接口,代理類通過InvocationHandler.invoke實現對目標類方法的調用。
CGLIB動態代理
CGLIB代理是通過使用一個字節碼處理框架ASM,來轉換字節碼並生成新的類,並在子類中采用方法攔截的技術攔截所有父類方法的調用,實現織如如橫切邏輯 ,效率上比使用反射技術的JDK動態代理要高,但是由於CGLIB的原理是動態為目標類生成子類代理類,所以不能為聲明為final的方法進行代理。其使用主要涉及兩個類:
MethodInterceptor接口:該接口提供一個方法intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3)主要用於攔截目標類方法的調用
Object arg0, :被代理的目標類
Method arg1, 委托方法
Object[] arg2, 方法參數
MethodProxy arg3 :代理方法的MethodProxy對象
Enhancer類:用於創建代理類
示例:
實現MethodInterceptor接口
package com.sl.aop; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; public class CglibServiceProxy implements MethodInterceptor { private Object targetClass; private OrderLogger orderLogger; public CglibServiceProxy(Object targetClass,OrderLogger orderLogger) { this.targetClass = targetClass; this.orderLogger = orderLogger; } /** * 創建代理對象 * */ public Object getInstance() { Enhancer enhancer = new Enhancer(); //設置目標類(需要被代理的類) enhancer.setSuperclass(this.targetClass.getClass()); // 回調方法 enhancer.setCallback(this); // 創建代理對象 return enhancer.create(); } /** * 攔截所有目標類方法的調用 * */ @Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { orderLogger.beforeCreateOrder(); Object o1 = arg3.invokeSuper(arg0, arg2); orderLogger.afterCreateOrder(); return o1; } }
測試方法:
public void Testdynamicproxy() { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class"); OrderServiceImpl serviceImpl = new OrderServiceImpl(); OrderLogger logger = new OrderLogger(); CglibServiceProxy proxy = new CglibServiceProxy(serviceImpl,logger); //通過生成子類的方式創建代理類 OrderServiceImpl proxyImp = (OrderServiceImpl)proxy.getInstance(); proxyImp.createOrder(); }
結果:
最後兩種代理對比一下:
JDK動態代理:代理類與委托類實現同一接口,主要是通過代理類實現InvocationHandler並重寫invoke方法來進行動態代理的,在invoke方法中將對方法進行增強處理 優點:不需要硬編碼接口,代碼復用率高,缺點:只能夠代理實現了接口的委托類
CGLIB動態代理:
代理類將委托類作為自己的父類並為其中的非final委托方法創建兩個方法,一個是與委托方法簽名相同的方法,它在方法中會通過super調用委托方法;另一個是代理類獨有的方法。在代理方法中,它會判斷是否存在實現了MethodInterceptor接口的對象,若存在則將調用intercept方法對委托方法進行代理 優點:可以在運行時對類或者是接口進行增強操作,且委托類無需實現接口,缺點:不能對final類以及final方法進行代理Spring溫故而知新 – AOP代理