本文內容
- 如何聲明通知
- 如何傳遞參數到通知方法中
- 多種通知多個切面的通知順序
- 多個切面通知的順序源碼分析與圖解
聲明通知
Spring中有5種通知,通過對應的注解來聲明:
@Before
Before 通知,用於方法執行前增強@AfterReturning
:After Returning 通知,方法正常執行返回后增強@AfterThrowing
:After Throwing 通知,方法執行通過拋出異常退出時@After
:After (Finally) 通知,方法執行退出時執行增強,不管是正常返回,還是拋出異常退出,相當於try{}catch{}finally{}
中的finally
的語句。@Around
:Around 通知,最強大的通知,環繞在目標方法前后執行。它有機會在方法運行之前和之后進行工作,並確定該方法何時、如何以及是否真正開始運行
始終使用滿足您要求的最不強大的通知形式(也就是說,如果前置通知可以使用,請不要使用環繞通知)。
簡單的使用通過一個綜合實例來說明,具體的通知再詳細說明。
綜合案例
目標類,2個方法,一個正常執行,一個拋出異常。
package com.crab.spring.aop.demo03.advice;
/**
* @author zfd
* @version v1.0
* @date 2022/2/7 11:31
* @關於我 請關注公眾號 螃蟹的Java筆記 獲取更多技術系列
*/
public class Service1 {
/**
* 正常執行的方法
*/
public String hello(String name) {
System.out.println("hello " + name);
return "hello " + name + "!";
}
/**
* 執行拋出異常的方法
*/
public void throwException() {
System.out.println("throws a runtime exception");
throw new RuntimeException("方法執行異常了");
}
}
切面中的通知
@Aspect // 切面
public class CommonCaseAspect {
/**
* 公共切點
* 匹配Service1的所有方法
*/
@Pointcut("execution(* com.crab.spring.aop.demo03.advice.Service1.*(..))")
public void pc(){
}
/**
* 前置通知
*/
@Before("pc()")
public void before(JoinPoint joinpoint){
System.out.println("Before: " + joinpoint);
}
/**
* 返回通知
*/
@AfterReturning("pc()")
public void afterReturning(JoinPoint joinpoint){
System.out.println("AfterReturning: " + joinpoint);
}
/**
* 拋出異常通知
*/
@AfterThrowing("pc()")
public void afterThrowing(JoinPoint joinpoint){
System.out.println("AfterThrowing: " + joinpoint);
}
/**
* 最終通知
*/
@After("pc()")
public void after(JoinPoint joinpoint){
System.out.println("After: " + joinpoint);
}
/**
* 最終通知
*/
@Around("pc()")
public Object around(ProceedingJoinPoint pdj) throws Throwable {
System.out.println("Around start: " + pdj);
Object ret = pdj.proceed();
System.out.println("Around end: " + pdj);
return ret;
}
public static void main(String[] args) {
Service1 target = new Service1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
// 添加切面
proxyFactory.addAspect(CommonCaseAspect.class);
Service1 proxy = proxyFactory.getProxy();
// 方法調用
proxy.hello("xx");
System.out.println("\n執行異常的結果:");
proxy.throwException();
}
}
正常執行方法的結果,不會觸發 @AfterThrowing 通知
Around start: execution(void com.crab.spring.aop.demo03.advice.Service1.hello(String))
Before: execution(void com.crab.spring.aop.demo03.advice.Service1.hello(String))
hello xx
AfterReturning: execution(void com.crab.spring.aop.demo03.advice.Service1.hello(String))
After: execution(void com.crab.spring.aop.demo03.advice.Service1.hello(String))
Around end: execution(void com.crab.spring.aop.demo03.advice.Service1.hello(String)
方法執行異常的結果,不會觸發@AfterReturning通知
執行異常的結果:
Around start: execution(void com.crab.spring.aop.demo03.advice.Service1.throwException())
Before: execution(void com.crab.spring.aop.demo03.advice.Service1.throwException())
throws a runtime exception
AfterThrowing: execution(void com.crab.spring.aop.demo03.advice.Service1.throwException())
After: execution(void com.crab.spring.aop.demo03.advice.Service1.throwException())
Exception in thread "main" java.lang.RuntimeException: 方法執行異常了
@Before
前置通知比較簡單,不深入講解。
@After
最終通知比較簡單,不深入講解。
@AfterReturning
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterReturning {
String pointcut() default "";
// 返回值名稱
String returning() default "";
}
可以通過returning
指定注入的返回值的名稱,需要注意的是通知方法中的返回值類型,只有返回類型和指定的類型或其子類型一致時,通知方法才會執行。使用Object可接收所有返回類型。
案例說明
定義目標對象3個方法,返回值類型分別是String、Long、void。
public class Service2 {
public String getString() {
System.out.println("Service2 getString");
return "xxx";
}
public Long getLong() {
System.out.println("Service2 getLong");
return 100L;
}
public void m() {
System.out.println("Service2 m");
}
}
通知和測試方法
@Aspect
public class AfterReturningAdvice {
/**
*
*/
@Pointcut("execution(* com.crab.spring.aop.demo03.advice.Service2.*(..))")
public void pc(){
}
/**
* 返回通知通過獲取returning返回值名稱,
* 注意方法中的第二個參數的類型,僅返回指定類型的值的方法才會增強
*/
@AfterReturning(pointcut = "pc()", returning = "retVal")
public void afterReturning1(JoinPoint joinpoint, Object retVal) {
System.out.println("AfterReturning 返回 Object : " + retVal);
}
/**
* 返回通知通過獲取returning返回值名稱,
* 注意方法中的第二個參數的類型,僅返回指定類型String的值的方法才會增強
*/
@AfterReturning(pointcut = "pc()", returning = "retVal")
public void afterReturning1(JoinPoint joinpoint, String retVal) {
System.out.println("AfterReturning 返回 String :" + retVal);
}
public static void main(String[] args) {
Service2 target = new Service2();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AfterReturningAdvice.class);
Service2 service2 = proxyFactory.getProxy();
service2.getString();
service2.getLong();
service2.m();
}
}
觀察下測試結果
Service2 getString
AfterReturning 返回 Object : xxx
AfterReturning 返回 String :xxx
Service2 getLong
AfterReturning 返回 Object : 100
Service2 m
AfterReturning 返回 Object : null
afterReturning1
只攔截返回值為String的方法getString()
的執行。
`afterReturning2 攔截所有方法的執行。
@AfterThrowing
public @interface AfterThrowing {
String pointcut() default "";
// 指定拋出的異常參數名稱
String throwing() default "";
throwing
可以指定注入到通知方法中的異常參數的名稱,同時異常參數的類型會限制方法匹配,只有返回指定異常類型或是其子類型才會執行增強方法。java.lang.Throwable
匹配所有異常類型。
直接看下案例
public class Service3 {
public void m(){
throw new IllegalStateException("自定義拋出IllegalStateException");
}
public void m2(){
throw new RuntimeException("自定義拋出RuntimeException");
}
}
@Aspect
public class AfterThrowingAdvice {
/**
*
*/
@Pointcut("execution(* com.crab.spring.aop.demo03.advice.Service3.*(..))")
public void pc(){
}
/**
* throwing指定異常參數名稱
* 匹配 IllegalStateException
* @param joinpoint
* @param ex
*/
@AfterThrowing(pointcut = "pc()", throwing = "ex")
public void afterThrowing1(JoinPoint joinpoint, IllegalStateException ex) {
System.out.println("AfterThrowing 異常類型 : " + ex);
}
@AfterThrowing(pointcut = "pc()", throwing = "ex")
public void afterThrowing2(JoinPoint joinpoint, Throwable ex) {
System.out.println("AfterThrowing 異常類型 : " + ex);
}
public static void main(String[] args) {
Service3 target = new Service3();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AfterThrowingAdvice.class);
Service3 service3 = proxyFactory.getProxy();
// service3.m();
service3.m2();
}
}
觀察下 service3.m()
的輸出結果,2個攔截通知都執行了。
AfterThrowing 異常類型 : java.lang.IllegalStateException: 自定義拋出IllegalStateException
AfterThrowing 異常類型 : java.lang.IllegalStateException: 自定義拋出IllegalStateException
Exception in thread "main" java.lang.IllegalStateException: 自定義拋出IllegalStateException
at com.crab.spring.aop.demo03.advice.Service3.m(Service3.java:11)
觀察下service3.m2();
輸出結果,只有afterThrowing1
沒有匹配到故不執行通知。
AfterThrowing 異常類型 : java.lang.RuntimeException: 自定義拋出RuntimeException
Exception in thread "main" java.lang.RuntimeException: 自定義拋出RuntimeException
@Around
使用 @Around 注釋聲明環繞通知。通知方法的第一個參數必須是 ProceedingJoinPoint
類型。在通知方法體中,對 ProceedingJoinPoint
調用proceed()
會導致底層目標方法運行。
常用的場景是方法的統計耗時,或是緩存層攔截方法的執行,直接返回緩存的數據,而不執行目標方法。
案例 統計耗時
@Aspect
public class AroundAdvice {
static class MyService {
public String getVal(String name) {
System.out.println("MyService getVal");
return UUID.randomUUID().toString();
}
}
/**
*
*/
@Pointcut("execution(* com.crab.spring.aop.demo03.advice.AroundAdvice.MyService.*(..))")
public void pc() {
}
@Around("pc()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("統計耗時開始");
long start = System.nanoTime(); //統計耗時開始
Object retVal = joinPoint.proceed();
long end = System.nanoTime();
System.out.println("統計耗時結束");
System.out.println("方法執行耗時納秒:" + (end - start));
return retVal;
}
public static void main(String[] args) {
MyService target = new MyService();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AroundAdvice.class);
MyService service2 = proxyFactory.getProxy();
service2.getVal("xx");
}
}
觀察下輸出結果
統計耗時開始
MyService getVal
統計耗時結束
方法執行耗時納秒:87827000
通知參數
JoinPoint 接口獲取信息
上面的例子中,任何通知方法都可以聲明 org.aspectj.lang.JoinPoint
類型的參數作為其第一個參數,當然環繞通知第一個參數類型是ProceedingJoinPoint
,它是 JoinPoint
的子類。
JoinPoint 提供了一些方法來提供對連接點可用狀態和關於連接點的靜態信息的反射訪問,其主要源碼如下。
public interface JoinPoint {
// 打印所有通知方法的有用描述
String toString();
// 獲取代理對象
Object getThis();
// 獲取目標對象
Object getTarget();
// 獲取所有的方法參數
Object[] getArgs();
// 返回被增強的方法的描述
Signature getSignature();
}
args傳參
之前@AfterReturning
可通過retVal
將方法結果當做參數傳遞給通知方法,@AfterThrowing
可通過throwing
將拋出的異常當做參數傳遞給通知方法。切點表達式args
也可以傳遞參數給通知方法。如果在 args
表達式中使用參數名稱代替類型名稱,則在調用通知時,相應參數的值將作為參數值傳遞。來看一個案例來理解。
定義一個參數對象
public class Account {
private String name;
private String password;
// ...
}
使用args
指定參數名
@Aspect
public class UseArgs {
static class MyAccountService {
public void validateAccount(Account account) {
System.out.println("校驗Account :" + account);
}
}
/**
*
*/
@Pointcut("execution(* com.crab.spring.aop.demo03.advice.parameter.UseArgs.MyAccountService.*(..))")
public void pc(){
}
/**
* args表達式不再指定參數類型,而是指定了傳遞到通知方法中的參數名稱,參數類型在通知方法中定義了
* 此處指定了Account類型參數為account
* @param account
*/
@Before("pc() && args(account,..)")
public void validateAccountBefore(JoinPoint joinPoint, Account account) {
System.out.println("前置通知,傳遞的account參數: " + account);
}
public static void main(String[] args) {
MyAccountService target = new MyAccountService();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(UseArgs.class);
MyAccountService proxy = proxyFactory.getProxy();
proxy.validateAccount(new Account("xx", "oo"));
}
}
觀察下結果
前置通知,傳遞的account參數: Account{name='xx', password='oo'}
校驗Account :Account{name='xx', password='oo'}
在前置通知方法中,已經可以獲取到通過args
傳遞的參數了。
@annotaion 傳參
類似args
表達式進行類型匹配可傳遞參數,@annotaion
匹配模板方法的的注解類型也可以以同樣的的方式進行傳參。通過案例了解下。
自定義注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Auditable {
int value();
}
@annotation
不指定注解類型,而是指定參數名稱
@Aspect
public class UseAnnotation {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Auditable {
int value();
}
static class MyAccountService {
@Auditable(100)
public void validateAccount(Account account) {
System.out.println("校驗Account :" + account);
}
}
/**
*
*/
@Pointcut("execution(* com.crab.spring.aop.demo03.advice.parameter.UseAnnotation.MyAccountService.*(..))")
public void pc() {
}
/**
* @annotation表達式不再指定目標方法包含的注解類型,而是指定了傳遞到通知方法中的參數名稱,參數類型在通知方法中定義了
* 此處指定了auditable參數,類型是注解 Auditable
*
* @param auditable
*/
@Before("pc() && @annotation(auditable)")
public void validateAccountBefore(JoinPoint joinPoint, Auditable auditable) {
System.out.println("前置通知,@annotation傳遞的auditable參數: " + auditable);
}
public static void main(String[] args) {
MyAccountService target = new MyAccountService();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(UseAnnotation.class);
MyAccountService proxy = proxyFactory.getProxy();
proxy.validateAccount(new Account("xx", "oo"));
}
}
觀察下輸出結果,通知方法可以獲取到作為參數傳遞的注解了。
前置通知,@annotation傳遞的auditable參數: @com.crab.spring.aop.demo03.advice.parameter.UseAnnotation$Auditable(value=100)
校驗Account :Account{name='xx', password='oo'}
擴展一下:其它的匹配類型的切點表達式都可以通過類似的方法進行傳遞參數:
this
代理對象target
目標對象@within
@target
@args
傳遞泛型參數
支持傳遞泛型參數,單值指定具體類型生效,泛型集合傳遞無效。看下案例
定義一個泛型接口
public interface Sample<T> {
void sampleGenericMethod(T param);
void sampleGenericCollectionMethod(Collection<T> params);
}
來一個Account的具體類型的實現類
static class MyAccountService implements Sample<Account> {
public void validateAccount(Account account) {
System.out.println("校驗Account :" + account);
}
@Override
public void sampleGenericMethod(Account param) {
System.out.println("MyAccountService sampleGenericMethod :" + param);
}
@Override
public void sampleGenericCollectionMethod(Collection<Account> params) {
System.out.println("sampleGenericCollectionMethod: ");
params.forEach(System.out::println);
}
}
通過args
傳遞泛型參數和泛型集合參數
@Aspect
public class UseArgsGeneric {
static class MyAccountService implements Sample<Account> {
public void validateAccount(Account account) {
System.out.println("校驗Account :" + account);
}
@Override
public void sampleGenericMethod(Account param) {
System.out.println("MyAccountService sampleGenericMethod :" + param);
}
@Override
public void sampleGenericCollectionMethod(Collection<Account> params) {
System.out.println("sampleGenericCollectionMethod: ");
}
}
/**
* 匹配 Sample接口及其子類的sampleGenericMethod方法執行
*/
@Pointcut("execution(* com.crab.spring.aop.demo03.advice.parameter.Sample+.sampleGenericMethod(..))")
public void pc() {
}
/**
* 匹配 Sample接口及其子類的 sampleGenericCollectionMethod 方法執行
*/
@Pointcut("execution(* com.crab.spring.aop.demo03.advice.parameter.Sample+.sampleGenericCollectionMethod(..))")
public void pc2() {
}
/**
* args 傳遞泛型參數,參數類型指定具體的類型Account
*
*
* @param account
*/
@Before("pc() && args(account,..)")
public void before1(JoinPoint joinPoint, Account account) {
System.out.println("前置通知,傳遞的account參數: " + account);
}
/**
* args 傳遞泛型參數,參數類型指定具體的類型String
*
* @param account
*/
@Before("pc() && args(account,..)")
public void before2(JoinPoint joinPoint, String account) {
System.out.println("前置通知,傳遞的account參數: " + account);
}
/**
* 泛型集合無效
* @param col
*/
@Before("pc() && args(col,..)")
public void before3(JoinPoint joinPoint, Collection<?> col) {
System.out.println("前置通知,傳遞的集合參數: " + col);
}
public static void main(String[] args) {
MyAccountService target = new MyAccountService();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(UseArgsGeneric.class);
MyAccountService proxy = proxyFactory.getProxy();
proxy.sampleGenericMethod(new Account("xx", "oo"));
// before1將攔截
// 觀察下集合形式
List<Account> accountList = Arrays.asList(new Account("xx1", "00x"), new Account("00", "xx"), null);
proxy.sampleGenericCollectionMethod(accountList);
}
}
結果如下
前置通知,傳遞的account參數: Account{name='xx', password='oo'}
MyAccountService sampleGenericMethod :Account{name='xx', password='oo'}
sampleGenericCollectionMethod:
單值的具體類型參數成功傳遞,而泛型集合無效。
通知順序
采用結論先上,驗證程序后行,最后源碼分析收尾順序來說明。
同一個切面內不同通知類型的順序
-
方法正常執行通知順序
Around前操作 Before: 目標方法執行 AfterReturning After
-
方法拋出異常退出通知順序
Around前操作 Before 目標方法執行 AfterThrowing After
測試程序如下,含5種通知
package com.crab.spring.aop.demo03.advice.ordering;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
/**
* 同一個切面內不同通知類型的順序
* @author zfd
* @version v1.0
* @date 2022/2/7 11:34
* @關於我 請關注公眾號 螃蟹的Java筆記 獲取更多技術系列
*/
@Aspect // 切面
public class CommonAspect {
/**
* 公共切點
* 匹配Service1的所有方法
*/
@Pointcut("execution(* com.crab.spring.aop.demo03.advice.ordering.Service1.*(..))")
public void pc(){
}
/**
* 前置通知
*/
@Before("pc()")
public void before(JoinPoint joinpoint){
System.out.println("Before: " + joinpoint);
}
/**
* 返回通知
*/
@AfterReturning("pc()")
public void afterReturning(JoinPoint joinpoint){
System.out.println("AfterReturning: " + joinpoint);
}
/**
* 拋出異常通知
*/
@AfterThrowing("pc()")
public void afterThrowing(JoinPoint joinpoint){
System.out.println("AfterThrowing: " + joinpoint);
}
/**
* 最終通知
*/
@After("pc()")
public void after(JoinPoint joinpoint){
System.out.println("After: " + joinpoint);
}
/**
* 最終通知
*/
@Around("pc()")
public Object around(ProceedingJoinPoint pdj) throws Throwable {
System.out.println("Around start: " + pdj);
Object ret = pdj.proceed();
System.out.println("Around end: " + pdj);
return ret;
}
public static void main(String[] args) {
Service1 target = new Service1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
// 添加切面
proxyFactory.addAspect(CommonAspect.class);
Service1 proxy = proxyFactory.getProxy();
// 方法調用
proxy.hello("xx");
System.out.println("\n執行異常的結果:");
proxy.throwException();
}
}
觀察下輸出結果,符合結論
Around start: execution(String com.crab.spring.aop.demo03.advice.ordering.Service1.hello(String))
Before: execution(String com.crab.spring.aop.demo03.advice.ordering.Service1.hello(String))
hello xx
AfterReturning: execution(String com.crab.spring.aop.demo03.advice.ordering.Service1.hello(String))
After: execution(String com.crab.spring.aop.demo03.advice.ordering.Service1.hello(String))
Around end: execution(String com.crab.spring.aop.demo03.advice.ordering.Service1.hello(String))
執行異常的結果:
Around start: execution(void com.crab.spring.aop.demo03.advice.ordering.Service1.throwException())
Before: execution(void com.crab.spring.aop.demo03.advice.ordering.Service1.throwException())
throws a runtime exception
AfterThrowing: execution(void com.crab.spring.aop.demo03.advice.ordering.Service1.throwException())
After: execution(void com.crab.spring.aop.demo03.advice.ordering.Service1.throwException())
Exception in thread "main" java.lang.RuntimeException: 方法執行異常了
多個切面之間的通知順序
- 切面級別的優先級可以通過注解
@Order
或是實現接口org.springframework.core.Ordered
,數值越小優先級越高。 - 類似洋蔥圈,前置方向的優先級越高,后置方向的優先級越低。
結論如下(以2個切面為例)
-
方法正常執行
Around1 start Before1 Around2 start Before2 目標方法執行 AfterReturning2 After2 Around2 end AfterReturning Afte Around end
-
方法異常退出
執行異常的結果: Around start Before Around2 start Before2 目標方法執行並拋出異常 AfterThrowing2 After2 AfterThrowing After
通知順序源碼分析
之前的2篇源碼分析從對象生成和代理方法執行流程分析得比較清晰了,一篇是使用編程式的AOP代理,另外一篇是使用@Aspect聲明式AOP代理。所以這里的源碼分析着重點是在第二篇的基礎上看下注解式的通知方法是如何轉換成有序的Advios鏈,再到有序的MethodInterceptor
鏈,如何執行的。
BeanPostProcessor觸發代理對象的生成
之前的分析說過Spring中AOP代理的對象的通過AbstractAutoProxyCreator
這個BeanPostProcessor
生成的,就已這個為切入點,看一下關鍵方法。
AbstractAutoProxyCreator#postProcessAfterInitialization
方法
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// bean初始化后為需要代理的bean的創建代理對象。
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
``AbstractAutoProxyCreator#wrapIfNecessary`方法
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 1、 前面是判斷是否需要新創建代理對象
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 2、獲取所有用於增強當前bean的Advisor鏈
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 3、創建AOP代理對象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
保持耐心,繼續往下看下Advisor鏈的獲取。
獲取所有用於增強當前bean的Advisor鏈
子類AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
方法獲取所有用於增強當前bean的Advisor鏈
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 1、查找符合的Advisor鏈
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
方法查找符合的Advisor鏈
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 1、獲取候選的Advisos,實際調用其子類的方法實現,InstantiationModelAwarePointcutAdvisorImpl對象列表
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 2、進行篩選Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 3、添加特殊的 ExposeInvocationInterceptor.ADVISOR 是DefaultPointcutAdvisor對象
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 4、關鍵的對Advisor進行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
候選的Advisors主要是子類AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
方法實現。
@Override
protected List<Advisor> findCandidateAdvisors() {
// 父類查找Advisor
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
// 從容器中生成 Advisors
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
往下看如何從從容器中生成 Advisors。
容器中切面類中的通知如何生成Advisor
BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors()
從切面生成Advisor
public List<Advisor> buildAspectJAdvisors() {
//1、 保存已經找到的切面bean名稱,防止每一次都循環一遍所有bean
List<String> aspectNames = this.aspectBeanNames;
// PS: 是否想起了單例模式的雙重檢查?
// 解析切面beanName並緩存,同時生成Adivsor
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
// 2、 循環容器中的beanNames列表
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
Class<?> beanType = this.beanFactory.getType(beanName, false);
if (beanType == null) {
continue;
}
// 3、 bean上面有Aspect就是切面
if (this.advisorFactory.isAspect(beanType)) {
// 添加切面bean緩存
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 4、關鍵的單個切面的所有通知轉換成Advisor鏈
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
// 4、關鍵的單個切面的所有通知轉換成Advisor鏈
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
// 緩存過切面beanName 直接生成Advisor
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
單個切面的通知方法是轉換成有序的Advisor
鏈的?接着往下看。
單個切面類內的通知如何轉成有序的Advisor
鏈
ReflectiveAspectJAdvisorFactory#getAdvisors
方法獲取Advisor
鏈
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new ArrayList<>();
// 1、getAdvisorMethods 獲取@Aspect切面中所有通知方法
for (Method method : getAdvisorMethods(aspectClass)) {
// 2、getAdvisor轉換成Advisor對象,InstantiationModelAwarePointcutAdvisorImpl
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
// 3、添加到 Advisor鏈
advisors.add(advisor);
}
}
// ...
return advisors;
}
ReflectiveAspectJAdvisorFactory#getAdvisorMethods
方法,獲取所有通知方法
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new ArrayList<>();
// 1、獲取所有通知方法
ReflectionUtils.doWithMethods(aspectClass, method -> {
// Exclude pointcuts
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
}, ReflectionUtils.USER_DECLARED_METHODS);
if (methods.size() > 1) {
// 2、關鍵將通知方法排序的排序器
methods.sort(METHOD_COMPARATOR);
}
return methods;
}
通知方法是如何包裝成MethodInterceptor
的再到Advisor?
ReflectiveAspectJAdvisorFactory#getAdvisor()
方法
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
// 此處轉換成 InstantiationModelAwarePointcutAdvisorImpl,里面將通知方法轉換成具體通知
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
InstantiationModelAwarePointcutAdvisorImpl
實現了Advisor
接口
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
// 省略...
else {
// A singleton aspect.
this.pointcut = this.declaredPointcut;
this.lazy = false;
// 將通知方法實例化為通知攔截器MethodInterceptor類
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
}
看下InstantiationModelAwarePointcutAdvisorImpl#instantiateAdvice
方法
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
// 干活的是AspectJAdvisorFactory
Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
return (advice != null ? advice : EMPTY_ADVICE);
}
看下干活的ReflectiveAspectJAdvisorFactory#getAdvice()
方法是如何將通知方法實例化為通知攔截器MethodInterceptor類的。
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
AbstractAspectJAdvice springAdvice;
// 1、獲取注解
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
// @Around -> AspectJAroundAdvice
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
// @Before -> AspectJMethodBeforeAdvice
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
// @After -> AspectJAfterAdvice
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
// @AfterReturning -> AspectJAfterReturningAdvice
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
// @AfterThrowing -> AspectJAfterThrowingAdvice
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
所以注解通知方法會轉成對應的通知類,對應關系如下:
@Around -> AspectJAroundAdvice
@Before -> AspectJMethodBeforeAdvice
@After -> AspectJAfterAdvice
@AfterReturning -> AspectJAfterReturningAdvice
@AfterThrowing -> AspectJAfterThrowingAdvice
接下是切面內的Advisor對應的方法的排序,關鍵的METHOD_COMPARATOR
通知排序器
private static final Comparator<Method> METHOD_COMPARATOR;
static {
// Note: although @After is ordered before @AfterReturning and @AfterThrowing,
// an @After advice method will actually be invoked after @AfterReturning and
// @AfterThrowing methods due to the fact that AspectJAfterAdvice.invoke(MethodInvocation)
// invokes proceed() in a `try` block and only invokes the @After advice method
// in a corresponding `finally` block.
Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
new InstanceComparator<>(
// 此處是通知方法的排序
Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
(Converter<Method, Annotation>) method -> {
AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
return (ann != null ? ann.getAnnotation() : null);
});
Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
}
單個切面的通知轉換成有序的Advisors了,循環多個切面添加到統一的Adivisors鏈中。
此時是會發現:局部單個切面的Advisor有序,整體多個切面的所有Advisor是無序的,需要再來一次排序。
多個切面生成的Advisor鏈是如何排序的
回到AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
方法
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 1、獲取候選的Advisos,實際調用其子類的方法實現
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 局部單個切面的Advisor有序,整體多個切面的所有Advisor是無序的,需要再來一次排序!
// 2、進行篩選Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 3、添加特殊的 ExposeInvocationInterceptor.ADVISOR 是DefaultPointcutAdvisor對象
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 4、關鍵的對Advisor進行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
整體的Advisor鏈排序的職責是由其子類AspectJAwareAdvisorAutoProxyCreator#sortAdvisors
方法實現的
protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
// 用holder包裝一下
List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors = new ArrayList<>(advisors.size());
for (Advisor advisor : advisors) {
partiallyComparableAdvisors.add(
new PartiallyComparableAdvisorHolder(advisor, DEFAULT_PRECEDENCE_COMPARATOR)); // 1 此處添加了一個排序器
}
// 2 使用排序器進行排序
List<PartiallyComparableAdvisorHolder> sorted = PartialOrder.sort(partiallyComparableAdvisors);
if (sorted != null) {
List<Advisor> result = new ArrayList<>(advisors.size());
for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {
//3 將排序的后的advisor返回
result.add(pcAdvisor.getAdvisor());
}
return result;
}
else {
return super.sortAdvisors(advisors);
}
}
排序器DEFAULT_PRECEDENCE_COMPARATOR
是AnnotationAwareOrderComparator
對象
private static final Comparator<Advisor> DEFAULT_PRECEDENCE_COMPARATOR = new AspectJPrecedenceComparator();
/**
* Create a default {@code AspectJPrecedenceComparator}.
*/
public AspectJPrecedenceComparator() {
this.advisorComparator = AnnotationAwareOrderComparator.INSTANCE;
}
AnnotationAwareOrderComparator
是Spring提供的一個排序器,用處非常廣泛。AnnotationAwareOrderComparator
是OrderComparator
的擴展,它支持Spring的org.springframework.core.Ordered
接口以及@Order
和@Priority
注解。
package org.springframework.core.annotation;
public class AnnotationAwareOrderComparator extends OrderComparator {
// 實例
public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
/**
* 除了超類中的org.springframework.core.Ordered檢查外,
* 這個實現還檢查各種類型的元素的@Order或@Priority
*/
@Override
@Nullable
protected Integer findOrder(Object obj) {
Integer order = super.findOrder(obj);
if (order != null) {
return order;
}
return findOrderFromAnnotation(obj);
}
// 從 @Order或@Priority 獲取排序數值
@Nullable
private Integer findOrderFromAnnotation(Object obj) {
AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());
MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);
Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
if (order == null && obj instanceof DecoratingProxy) {
return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());
}
return order;
}
@Override
@Nullable
public Integer getPriority(Object obj) {
if (obj instanceof Class) {
return OrderUtils.getPriority((Class<?>) obj);
}
Integer priority = OrderUtils.getPriority(obj.getClass());
if (priority == null && obj instanceof DecoratingProxy) {
return getPriority(((DecoratingProxy) obj).getDecoratedClass());
}
return priority;
}
public static void sort(List<?> list) {
if (list.size() > 1) {
list.sort(INSTANCE);
}
}
public static void sort(Object[] array) {
if (array.length > 1) {
Arrays.sort(array, INSTANCE);
}
}
public static void sortIfNecessary(Object value) {
if (value instanceof Object[]) {
sort((Object[]) value);
}
else if (value instanceof List) {
sort((List<?>) value);
}
}
}
通過獲取切面類上的的org.springframework.core.Ordered
接口或是@Order
和@Priority
注解對應的排序值,排序后就可以得到整體優先級由高到低的有序的Advisor鏈。
舉個例子,假設Aspect1
的優先級是1,Aspect2
的優先級是2,那么最終的Advisor鏈如下。
ExposeInvocationInterceptor.ADVISOR(DefaultPointcutAdvisor)
Aspect1的:
(AspectJAroundAdvice) InstantiationModelAwarePointcutAdvisorImpl
(AspectJMethodBeforeAdvice) InstantiationModelAwarePointcutAdvisorImpl
(AspectJAfterAdvice) InstantiationModelAwarePointcutAdvisorImpl
(AspectJAfterReturningAdvice) InstantiationModelAwarePointcutAdvisorImpl
(AspectJAfterThrowingAdvice) InstantiationModelAwarePointcutAdvisorImpl
Aspect2的:
(AspectJAroundAdvice) InstantiationModelAwarePointcutAdvisorImpl
(AspectJMethodBeforeAdvice) InstantiationModelAwarePointcutAdvisorImpl
(AspectJAfterAdvice) InstantiationModelAwarePointcutAdvisorImpl
(AspectJAfterReturningAdvice) InstantiationModelAwarePointcutAdvisorImpl
(AspectJAfterThrowingAdvice) InstantiationModelAwarePointcutAdvisorImpl
MethodInterceptor
鏈執行過程
此處的代理對象是通過CGLIB 方式創建的代理,所以從CglibAopProxy#getProxy()
方法入手。
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
try {
// ... 省略非關注的代碼
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
// GGLIB的關鍵是回調的設置
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
}
關鍵方法是回調的設置
CglibAopProxy#getProxy()
方法,關注關鍵的攔截器設置,刪除了部分不關注代碼
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Choose an "aop" interceptor (used for AOP calls).
// AOP相關的攔截器設置 advised 就是AdvisedSupport對象,熟悉的代理配置類
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
Callback[] mainCallbacks = new Callback[] {
aopInterceptor, // for normal advice
targetInterceptor, // invoke target without considering advice, if optimized
new SerializableNoOp(), // no override for methods mapped to this
targetDispatcher, this.advisedDispatcher,
new EqualsInterceptor(this.advised),
new HashCodeInterceptor(this.advised)
};
Callback[] callbacks;
callbacks = mainCallbacks;
return callbacks;
}
上面將就是AdvisedSupport對象傳遞給了DynamicAdvisedInterceptor對象。DynamicAdvisedInterceptor應該不陌生,CGLIB的通用的AOP攔截器,代理方法的調用會觸發該攔截器的intercept
方法。
DynamicAdvisedInterceptor#intercept
方法
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 1、關鍵的從AdvisedSupport中的Advisor鏈獲取MethodInterceptor鏈
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
try {
retVal = methodProxy.invoke(target, argsToUse);
}
catch (CodeGenerationException ex) {
CglibMethodInvocation.logFastClassGenerationFailure(method);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
}
else {
// We need to create a method invocation...
// 2、包裝成 CglibMethodInvocation並執行proceed()
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
上面的關鍵是方法:
-
關鍵的從AdvisedSupport中的Advisor鏈獲取MethodInterceptor鏈
主要邏輯是之前分析過的使用適配DefaultAdvisorAdapterRegistry將Advisor中通知包裝成對應的
MethodInterceptor
類。不過此處注意的是Advisor中的部分通知在前面已經包裝成MehtodIntertor
的子類對象了,此處就不需要適配轉換了,否則需要適配轉換如下@Around -> AspectJAroundAdvice @Before -> AspectJMethodBeforeAdvice-> MethodBeforeAdviceInterceptor @After -> AspectJAfterAdvice @AfterReturning -> AspectJAfterReturningAdvice->AfterReturningAdviceInterceptor @AfterThrowing -> AspectJAfterThrowingAdvice
-
包裝成 CglibMethodInvocation並執行proceed(),其實最終執行就是我們分析中的
ReflectiveMethodInvocation.procced
,將MehtodIntertor
攔截器鏈有序地執行@Override @Nullable public Object proceed() throws Throwable { // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } // 1 獲取攔截器鏈 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); // 省略 else { // 2 依次執行攔截器鏈 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
5個通知類解析
以單個切面內的通知順序開始着手調試,直接將斷點打到org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
可以看到將執行的攔截器依次如下:
ExposeInvocationInterceptor
ApectJAroundAdvice
MethodBeforeAdviceInterceptor
ApectJAfterAdvice
AfterReturningAdviceInterceptor
ApectJAfterThrowingAdvice
第一個之前說過是附加的特殊的攔截器,可以先忽略,來看下其它5個攔截器的類圖和對應的通知。
AspectJAroundAdvice 類
注意觀察執行過程,后面的類類似
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {
public AspectJAroundAdvice(
Method aspectJAroundAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJAroundAdviceMethod, pointcut, aif);
}
@Override
public boolean isBeforeAdvice() {
return false;
}
@Override
public boolean isAfterAdvice() {
return false;
}
@Override
protected boolean supportsProceedingJoinPoint() {
return true;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
// 1 調用around的增強方法
return invokeAdviceMethod(pjp, jpm, null, null);
}
}
MethodBeforeAdviceInterceptor 類
注意觀察執行過程,后面的類類似
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
private final MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 1 執行前置通知
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 2 進入下一個攔截器
return mi.proceed();
}
}
AspectJAfterAdvice 類
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 1 先執行下一個攔截器鏈 在try-finally中
return mi.proceed();
}
finally {
// 2 調用最終通知方法
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
}
AfterReturningAdviceInterceptor 類
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
private final AfterReturningAdvice advice;
public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
//1 先執行下一個攔截器,等待返回結果
Object retVal = mi.proceed();
// 2 后執行返回通知
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
// 3 返回通知處理后的結果
return retVal;
}
}
AspectJAfterThrowingAdvice 類
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
public AspectJAfterThrowingAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public boolean isBeforeAdvice() {
return false;
}
@Override
public boolean isAfterAdvice() {
return true;
}
@Override
public void setThrowingName(String name) {
setThrowingNameNoCheck(name);
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 1 先執行下一個通知鏈 在try-catch
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
// 2 后執行異常拋出后通知
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
/**
* 通知類型是否和@AfterThrowing.throwing配置的通知類型匹配
*/
private boolean shouldInvokeOnThrowing(Throwable ex) {
return getDiscoveredThrowingType().isAssignableFrom(ex.getClass());
}
}
圖解模擬
單個切面5個通知
- 紫色:攔截器鏈自上而下遞歸執行
- 藍色:目標方法無異常,攔截器鏈自下而上遞歸返回
- 紅色:目標方法有異常,攔截器鏈自下而上遞歸返回
結合上面的圖和5個通知的invoke
方法邏輯,偽代碼如下:
{
Around start
{
Before invoke()
{
try{
{
{
try{
目標方法執行()
}catche(Throwable ex){
AfterThrowing.invoke()
}
}
AfterReturning invoke()
}
} finally{
After invoke()
}
}
}
Around end
}
2個切面10個通知
2個切面10個通知的執行圖解如下:
結合上面的圖和5個通知的invoke
方法邏輯,偽代碼如下:
{
Around start
{
Before invoke()
{
try{
{
{
try{
// 目標方法執行() 此處套娃下一個切面的通知
{
Around2 start
{
Before2 invoke()
{
try{
{
{
try{
目標方法執行()
}catche(Throwable ex){
AfterThrowing2.invoke()
}
}
AfterReturning2 invoke()
}
} finally{
After2 invoke()
}
}
}
Around2 end
}
}catche(Throwable ex){
AfterThrowing.invoke()
}
}
AfterReturning invoke()
}
} finally{
After invoke()
}
}
}
Around end
}
總結
本文介紹如何聲明通知、如何傳遞參數到通知方法中、多種通知多個切面的通知順序源碼分析與圖解。
本篇源碼地址:https://github.com/kongxubihai/pdf-spring-series/blob/main/spring-series-aop/src/main/java/com/crab/spring/aop/demo03/advice
知識分享,轉載請注明出處。學無先后,達者為先!