Spring AOP 切面執行順序
1. 概述
1.1 術語
Spring AOP 的相關術語:
- Aspect:切面,由一系列切點、增強和引入組成的模塊對象,可定義優先級,從而影響增強和引入的執行順序。事務管理(Transaction management)在java企業應用中就是一個很好的切面樣例。
- Join point:接入點,程序執行期的一個點,例如方法執行、類初始化、異常處理。 在Spring AOP中,接入點始終表示方法執行。
- Advice:增強,切面在特定接入點的執行動作,包括 "around," "before" and "after"等多種類型。包含Spring在內的許多AOP框架,通常會使用攔截器來實現增強,圍繞著接入點維護著一個攔截器鏈。
- Pointcut:切點,用來匹配特定接入點的謂詞(表達式),增強將會與切點表達式產生關聯,并運行在任何切點匹配到的接入點上。通過切點表達式匹配接入點是AOP的核心,Spring默認使用AspectJ的切點表達式。
- Introduction:引入,為某個type聲明額外的方法和字段。Spring AOP允許你引入任何接口以及它的默認實現到被增強對象上。
- Target object:目標對象,被一個或多個切面增強的對象。也叫作被增強對象。既然Spring AOP使用運行時代理(runtime proxies),那么目標對象就總是代理對象。
- AOP proxy:AOP代理,為了實現切面功能一個對象會被AOP框架創建出來。在Spring框架中AOP代理的默認方式是:有接口,就使用基于接口的JDK動態代理,否則使用基于類的CGLIB動態代理。但是我們可以通過設置
proxy-target-class="true",完全使用CGLIB動態代理。 - Weaving:織入,將一個或多個切面與類或對象鏈接在一起創建一個被增強對象。織入能發生在編譯時 (compile time )(使用AspectJ編譯器),加載時(load time),或運行時(runtime) 。Spring AOP默認就是運行時織入,可以通過
枚舉AdviceMode來設置。
1.2 簡述
本次 Spring AOP 執行順序主要是針對同一切入點的不同切面執行順序。
Spring AOP 為定義切面的執行順序提供了兩種方案:
- 實現 Ordered 接口
- 使用 @Order 接口
2. 示例
2.1 實現 Ordered 接口
注解類
First
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RUNTIME)
public @interface First {
}
Sencod
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RUNTIME)
public @interface Second {
}
切面
FirstAspect
@Slf4j
@Component
@Aspect
public class FirstAspect implements Ordered {
@Pointcut("@annotation(com.booleandev.data.aop.First) || @within(com.booleandev.data.aop.First)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) {
log.info("--------------> ann,first注解執行");
try {
return pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
@Override
public int getOrder() {
return 1;
}
}
Second
@Slf4j
@Component
@Aspect
public class SecondAspect implements Ordered{
@Pointcut("@annotation(com.booleandev.data.aop.Second) || @within(com.booleandev.data.aop.Second)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) {
log.info("--------------> ann,Second執行");
try {
return pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
@Override
public int getOrder() {
return 2;
}
}
切入點
@Slf4j
@Service
public class UserService{
@Autowired
private UserRepository userRepository;
@First
@Second
public List<User> findAll() {
log.info(entityManager.toString());
return userRepository.findAll();
}
}
結果
--------------> ann,first注解執行
--------------> ann,Second執行
2.2 使用 @Order 接口
注解類
First
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RUNTIME)
public @interface First {
}
Sencod
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RUNTIME)
public @interface Second {
}
切面
FirstAspect
@Slf4j
@Component
@Aspect
@Order(1)
public class FirstAspect {
@Pointcut("@annotation(com.booleandev.data.aop.First) || @within(com.booleandev.data.aop.First)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) {
log.info("--------------> ann,first注解執行");
try {
return pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
}
SecondAspect
@Slf4j
@Component
@Aspect
@Order(2)
public class SecondAspect{
@Pointcut("@annotation(com.booleandev.data.aop.Second) || @within(com.booleandev.data.aop.Second)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) {
log.info("--------------> ann,Second執行");
try {
return pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
}
切入點
@Slf4j
@Service
public class UserService{
@Autowired
private UserRepository userRepository;
@First
@Second
public List<User> findAll() {
log.info(entityManager.toString());
return userRepository.findAll();
}
}
結果
--------------> ann,Second執行
--------------> ann,first注解執行
3. 結論
- 切面執行順序有兩種方式
- 實現 Ordered 接口
- 使用 @Order注解
- 排序為順序,數字越小,越先被執行
- 如果同時使用了注解和實現接口,則以接口的 order 為主
參考文檔:

浙公網安備 33010602011771號