Spring AOP詳解
圖解Spring AOP基礎概念
1、Spring AOP 是通過動態代理技術實現
Spring AOP 只是一個編程思想,切面編程。而實現"切面編程"是通過動態代理技術實現。動態代理技術目前常用的是JDK動態代理、CGlib動態代理技術。這兩種動態代理技術的原理不一樣。
1.1 JDK動態代理
JDK動態代理,是通過JDK原生的Proxy類實現。若是一個目標對象實現了接口,則Spring AOP默認使用JDK動態代理。如下圖所示:Spring AOP 的JDK代理,是通過Proxy實現。
- 1:要代理的目標 object至少 實現一個接口,則將使用 JDK 動態代理。目標類型實現的所有接口都將被代理。
被代理所有實現接口的的方法,但並不是每個方法都會被"增強",這就需要看"切點“的"連接點"配置了
@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())")
public class MyAspect {
private int someState;
@Before(com.xyz.myapp.SystemArchitecture.businessService())
//只有SystemArchitecture接口下的businessService()方法纔會被 @Before"增強",而其他方法只是被代理,
//並沒有增強
public void recordServiceUsage() {
// ...
}
}
- 2:Spring AOP 底層使用JDK動態代理:
- 3:JDK動態代理類源碼:
//接口
public interface Manager {
public void modify();
}
//動態代理類源碼
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//被代理類實現的接口Manager
public final class $Proxy0 extends Proxy implements Manager {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
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.ml.test.Manager").getMethod("modify",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
//代理類的構造函數
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] {
obj
}))
.booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//代理類通過字節碼生成的,實現接口方法同樣的方法
public final void modify() {
try {
...這裏可以加入"前置通知"...
//執行被代理類的方法
//從這裏就可以看到,最終調用的還是目標對象實現Manager接口的方法。
super.h.invoke(this, m3, null);
...這裏可以加入"後置通知"...
...這裏可以加入"返回通知"...
return;
} catch (Error e) {} catch (Throwable throwable) {
...這裏可以加入"執行錯誤通知"...
throw new UndeclaredThrowableException(throwable);
}finally{
...這裏可以加入"finally通知"...
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
1.2 CGLib動態代理
- 1:如果目標 object 沒有實現任何接口,那麼將創建 CGLIB 代理。
- 2:若是強制使用CGlib動態代理:
①、配置方式:Spring 配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>。
②、註解方式:@EnableAspectJAutoProxy(proxyTargetClass = ture) - 3:Cglib動態代理目標對象源碼:
package com.zhang.shine.cache;
import java.lang.reflect.Method;
import java.util.Map;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Dispatcher;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.framework.Advised;
//Sev是目標對象類,從這裏可以看出,gclib是通過繼承目標對象類的方式實現動態代理
public class Sev$$EnhancerByCGLIB$$e8033e73 extends Sev
implements SpringProxy, Advised, Factory {
private boolean CGLIB$BOUND;
private boolean CGLIB$CONSTRUCTED;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private MethodInterceptor CGLIB$CALLBACK_1;
private NoOp CGLIB$CALLBACK_2;
private Dispatcher CGLIB$CALLBACK_3;
private Dispatcher CGLIB$CALLBACK_4;
private MethodInterceptor CGLIB$CALLBACK_5;
private MethodInterceptor CGLIB$CALLBACK_6;
private static final Method CGLIB$getSort$0$Method;
private static final MethodProxy CGLIB$getSort$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$getSort$1$Method;
private static final MethodProxy CGLIB$getSort$1$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
private static final Method CGLIB$equals$5$Method;
private static final MethodProxy CGLIB$equals$5$Proxy;
private static final Method CGLIB$toString$6$Method;
private static final MethodProxy CGLIB$toString$6$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
Class localClass;
CGLIB$emptyArgs = new Object[0];
ClassLoader tmp27_17 = (localClass = Class.forName("com.zhang.shine.cache.Sev$$EnhancerByCGLIB$$e8033e73")).getClassLoader();
CGLIB$getSort$0$Proxy = MethodProxy.create(tmp27_17, (e8033e73.CGLIB$getSort$0$Method = Class.forName(
"com.zhang.shine.cache.Sev").getDeclaredMethod("getSort", new Class[] {
Integer.TYPE, Integer.TYPE
})).getDeclaringClass(), localClass, "(II)Ljava/util/Map;", "getSort", "CGLIB$getSort$0");
ClassLoader tmp74_27 = tmp27_17;
CGLIB$getSort$1$Proxy = MethodProxy.create(tmp74_27, (e8033e73.CGLIB$getSort$1$Method = Class.forName(
"com.zhang.shine.cache.Sev").getDeclaredMethod("getSort", new Class[0])).getDeclaringClass(),
localClass, "()V", "getSort", "CGLIB$getSort$1");
ClassLoader tmp109_74 = tmp74_27;
CGLIB$hashCode$3$Proxy = MethodProxy.create(tmp109_74, (e8033e73.CGLIB$hashCode$3$Method = Class.forName(
"java.lang.Object").getDeclaredMethod("hashCode", new Class[0])).getDeclaringClass(),
localClass, "()I", "hashCode", "CGLIB$hashCode$3");
ClassLoader tmp144_109 = tmp109_74;
CGLIB$clone$4$Proxy = MethodProxy.create(tmp144_109, (e8033e73.CGLIB$clone$4$Method = Class.forName(
"java.lang.Object").getDeclaredMethod("clone", new Class[0])).getDeclaringClass(), localClass,
"()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
ClassLoader tmp179_144 = tmp144_109;
CGLIB$equals$5$Proxy = MethodProxy.create(tmp179_144, (e8033e73.CGLIB$equals$5$Method = Class.forName(
"java.lang.Object").getDeclaredMethod("equals", new Class[] {
Class.forName("java.lang.Object")
})).getDeclaringClass(), localClass, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$5");
CGLIB$toString$6$Proxy = MethodProxy.create(tmp179_144, (e8033e73.CGLIB$toString$6$Method = Class.forName(
"java.lang.Object").getDeclaredMethod("toString", new Class[0])).getDeclaringClass(),
localClass, "()Ljava/lang/String;", "toString", "CGLIB$toString$6");
return;
}
final Map CGLIB$getSort$0(int paramInt1, int paramInt2) {
//在代理對象中直接調用父類(目標對象)的方法
return super.getSort(paramInt1, paramInt2);
}
2、Spring AOP 動態代理"增強"織入的時機
2.1 Spring AOP在對目標對象方法織入是在什麼時候發生的呢?在編譯器?在運行時?
通過分析動態代理原理和Spring AOP相關源碼,可以知道不管是JDK動態代理,還是CGlib動態代理都是在運行時對目標對象進行織入。
1、JDK、Cglib都是通過動態生成xxx.class文件的形式完成對目標對象的代理。
2、Spring AOP是需要與Spring IOC配合使用,在Spring IOC中需要先解析、生成原始的目標對象Bean,在生成Bean對象時,IOC的後置處理器會更具Bean是否被代理,爲其生成代理對象。
2.2 Spring AOP 中,什麼時候爲原生對象生成代理對象?
1、通過Spring AOP是需要與Spring IOC配合使用,在Spring IOC中需要先解析、生成原始的目標對象Bean,在生成Bean對象時,IOC的後置處理器會更具Bean是否被代理,爲其生成代理對象。 可以瞭解到是在Bean初始化時期爲其生成動態代理對象。而放入IOC中的也是動態代理對象,原生對象並沒有放入IOC容器中。
2、後續通過getBean()獲取的Bean都是代理對象。