1.概述
Aop(Aspect Oriented Programming),即面向切面編程萨惑,這是面向?qū)ο笏枷氲囊环N補(bǔ)充,面向切面編程就是在程序運(yùn)行時(shí),在不改變程序源碼的情況下沮榜,動態(tài)的增強(qiáng)方法的功能。
Aop的使用場景非常多喻粹,例如:日志 蟆融、事物等等
2.概念
Aop的世界有很多概念,其中重要的有切面守呜、通知型酥、連接點(diǎn)、切點(diǎn)查乒、織入弥喉。
切面(Aspect)
橫切關(guān)注點(diǎn)可以被模塊化為特殊的類,這些類被稱為切面
一個(gè)切面包括切點(diǎn)和通知
通知(Advice)
一個(gè)切面的工作被稱為通知玛迄,它定義了切面要增強(qiáng)的具體內(nèi)容
Spring切面可以有5種類型的通知:
-
前置通知(Before)
通知方法在目標(biāo)方法被調(diào)用前執(zhí)行 -
后置通知/最終通知(After)
通知方法在目標(biāo)方法返回或者拋出異常后執(zhí)行
注意
??:
1.不管目標(biāo)方法的返回值類型是否為void
2.不管目標(biāo)方法有沒有返回確切的值還是null
-
返回通知(After-returning)
通知方法在目標(biāo)方法返回后執(zhí)行
注意
??:
1.目標(biāo)方法的返回值類型不能為void
由境,否則無此通知
2.不管目標(biāo)方法有沒有返回確切的值還是null
-
異常通知(After-throwing)
通知方法在目標(biāo)方法拋出異常后執(zhí)行 -
環(huán)繞通知(Around)
通知包裹了被通知的方法,在被通知的方法調(diào)用之前和調(diào)用之后執(zhí)行自定義的行為
實(shí)際上就像在一個(gè)通知方法中同時(shí)編寫前置通知和后置通知
通俗的理解:通知定義了切面“干什么”憔晒、“什么時(shí)候干”的問題
連接點(diǎn)(Join point)
我們的應(yīng)用程序可能有各種時(shí)機(jī)應(yīng)用通知藻肄,這些時(shí)機(jī)被稱為連接點(diǎn)
切點(diǎn)(Pointcut)
一個(gè)切面并不是需要通知應(yīng)用中的所有連接點(diǎn)蔑舞,我們的某個(gè)切面真正通知的那個(gè)連接點(diǎn)被稱為切點(diǎn)
通俗的理解:切點(diǎn)定義了切面“在哪里干”的問題
織入(Weaving)
織入是把切面應(yīng)用到目標(biāo)對象并創(chuàng)建新的代理對象的過程
切面在指定的連接點(diǎn)織入到目標(biāo)對象中
在目標(biāo)對象的生命周期里有多個(gè)點(diǎn)可以進(jìn)行織入:
-
編譯期
切面在目標(biāo)類編譯時(shí)被織入
這種方式需要特殊的編譯器,AspectJ的織入編譯器就是以這種方式織入切面的 -
類加載期
切面在目標(biāo)類加載到JVM時(shí)被織入
這種方式需要特殊的類加載器(ClassLoader)嘹屯,它可以 在目標(biāo)類被引入應(yīng)用之前增強(qiáng)該目標(biāo)類的字節(jié)碼攻询,AspectJ5的加載時(shí)織入就支持以這種方式織入切面 -
運(yùn)行期
切面在應(yīng)用運(yùn)行的某個(gè)時(shí)刻被織入
一般情況下,在織入切面時(shí)州弟,容器會為目標(biāo)對象動態(tài)的創(chuàng)建一個(gè)代理對象
Spring AOP就是以這種方式織入切面的
3.Spring對AOP的支持
并不是所有的AOP框架都是相同的钧栖,它們在連接點(diǎn)模型上可能有強(qiáng)弱之分
有些框架允許在字段修飾符級別應(yīng)用通知,而有一些框架只支持方法調(diào)用級別的連接點(diǎn)婆翔。除此之外拯杠,它們織入切面的方式和時(shí)機(jī)也所有不同。
Spring和AspectJ項(xiàng)目之間有大量的協(xié)作啃奴,而且Spring對AOP的支持也在很多方面借鑒了AspectJ項(xiàng)目潭陪。
Spring提供了4種類型的AOP支持:
- 基于代理的經(jīng)典Spring AOP
- 純POJO切面
- @AspectJ注解驅(qū)動的切面
- 注入式AspectJ切面(適用于Spring各個(gè)版本)
SpringAOP構(gòu)建在動態(tài)代理的基礎(chǔ)上,因此Spring對AOP的支持局限于方法攔截
最蕾。
Spring的通知都是Java編寫的依溯,定義通知所應(yīng)用的切點(diǎn)通常會使用注解或者在Spring的配置文件里采用XML來編寫
Spring在運(yùn)行期把切面織入到Spring管理的bean中,實(shí)際上Spring是基于動態(tài)代理實(shí)現(xiàn)的瘟则,代理類封裝了目標(biāo)類黎炉,并攔截被通知方法的調(diào)用,再把調(diào)用轉(zhuǎn)發(fā)給真正的目標(biāo)bean醋拧,當(dāng)代理攔截到方法調(diào)用是慷嗜,在調(diào)用目標(biāo)bean方法之前會執(zhí)行切面邏輯。
4.基于注解聲明切面
在Spring AOP中丹壕,要使用AspectJ的切點(diǎn)表達(dá)式語言來定義切點(diǎn)庆械,但是Spring僅支持AspectJ切點(diǎn)指示器的一個(gè)子集
SpirngAOP支持的AspectJ切點(diǎn)指示器有:
AspectJ指示器 | 描述 |
---|---|
arg() | 限制連接點(diǎn)匹配參數(shù)為指定類型的執(zhí)行方法 |
@args() | 限制連接點(diǎn)匹配參數(shù)由指定注解標(biāo)注的執(zhí)行方法 |
execution() | 用于匹配是連接點(diǎn)的執(zhí)行方法 |
this() | 限制連接點(diǎn)匹配AOP代理的bean引用為指定類型的類 |
target | 限制連接點(diǎn)匹配目標(biāo)對象為指定類型的類 |
@target() | 限制連接點(diǎn)匹配特定的執(zhí)行對象,這些對象對應(yīng)的類要具有指定類型的注解 |
within() | 限制連接點(diǎn)匹配的指定類型 |
@withinn() | 限制連接點(diǎn)匹配指定注解所標(biāo)注的類型(當(dāng)使用Spring AOP時(shí)雀费,方法定義由指定的注解所標(biāo)注的類里) |
@annotation | 限定匹配帶有指定注解的連接點(diǎn) |
在Spring中嘗試使用AspectJ其他指示器干奢,將會拋出IllegalArgumentException異常
如上表格中所展示的Spring支持的指示器,注意只有execution
指示器是實(shí)際執(zhí)行匹配的盏袄,其他指示器都是用來限制匹配的(execution指示器是我們在編寫切點(diǎn)定義時(shí)最主要使用的指示器)
4.1編寫切點(diǎn)
為了演示Spring的AOP忿峻,我們定義了如下一個(gè)接口:
public interface IUserService {
/**
* 根據(jù)ID獲取用戶
*
* @param id 主鍵ID
* @return 用戶信息
*/
User getById(Integer id);
/**
* 保存用戶信息
*
* @param user 用戶信息
* @return 影響的行數(shù)
*/
int saveUser(User user);
}
假設(shè)我們希望在getById()方法觸發(fā)通知的調(diào)用,那么切點(diǎn)表達(dá)式寫法如下:
4.2使用注解創(chuàng)建切面
使用注解創(chuàng)建切面是AspectJ 5所引入的關(guān)鍵特性辕羽。
Spring的切面是一個(gè)純Java類逛尚,@Aspect注解能夠表明一個(gè)一個(gè)類不僅僅是一個(gè)POJO,還是一個(gè)切面:
package com.tp.aop;
import com.tp.models.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* FileName: UserServiceOperationAspect
* Author: TP
* Description:SpringAop-Service層切面測試
*/
@Slf4j
@Aspect
@Component
public class UserServiceOperationAspect {
/**
* 切點(diǎn)定義
*/
@Pointcut("execution(* com.tp.service.IUserService.getById(..))")
public void serviceGetById() {
}
/**
* 測試service前置通知
*
* @param id 用戶主鍵ID
*/
@Before("serviceGetById() && args(id)")
public void getUserBefore(Integer id) {
log.info(">>>>AOP-Service:前置通知刁愿,請求參數(shù)id為{}", id);
}
/**
* 測試service后置通知
*
* @param id 請求參數(shù)
*/
@After("serviceGetById() && args(id)")
public void getUserAfter(Integer id) {
log.info(">>>>AOP-Service:后置通知绰寞,請求參數(shù)id為{}", id);
}
/**
* 測試service返回通知
*
* @param id 主鍵ID
* @param user 用戶信息
*/
@AfterReturning(pointcut = "serviceGetById() && args(id)", returning = "user")
public void getUserAfterReturning(Integer id, User user) {
log.info(">>>>AOP-Service:返回通知,請求參數(shù)id為{},響應(yīng)結(jié)果User信息為:{}", id, user);
}
/**
* 測試service異常通知
*
* @param id 請求參數(shù)
*/
@AfterThrowing(pointcut = "serviceGetById() && args(id)", throwing = "e")
public void getUserAfterTrowing(Integer id, Exception e) {
log.info(">>>>AOP-Service:異常通知滤钱,請求參數(shù)id為{}觉壶,異常信息:{}", id, e);
}
/**
* 測試service環(huán)繞通知
* 需要注意的是,對于環(huán)繞通知必須給一個(gè)ProceedingJoinPoint參數(shù)件缸,并且放在參數(shù)的第一位
* 方法體中必須調(diào)用proceedingJoinPoint.proceed()方法铜靶,否則會阻塞目標(biāo)方法的執(zhí)行
*
* @param proceedingJoinPoint proceedingJoinPoint
* @param id 請求參數(shù)id
* @return 返回值,這里要注意:Around通知返回值不要寫成void(除非目標(biāo)方法返回值為void)他炊,否則最終返回結(jié)果為void
*/
@Around("serviceGetById() && args(id)")
public Object getUserAround(ProceedingJoinPoint proceedingJoinPoint, Integer id) throws Throwable{
log.info(">>>>AOP-Service:環(huán)繞通知-開始争剿,請求參數(shù)id為{}", id);
Object result = proceedingJoinPoint.proceed();
log.info(">>>>AOP-Service:環(huán)繞通知-結(jié)束,請求參數(shù)id為{}", id);
return result;
}
}
我們在serviceGetById()方法上添加了@Pointcut注解痊末,并且為@Pointcut注解設(shè)置的值是一個(gè)切點(diǎn)表達(dá)式蚕苇,serviceGetById()方法的實(shí)現(xiàn)并不重要,這個(gè)方法本身只是一個(gè)標(biāo)識凿叠,供@Pointcut依附涩笤。
同時(shí)我們需要將UserServiceOperationAspect聲明個(gè)一個(gè)Spring管理的bean,所以我們增加了注解@Component幔嫂。
如果你就此止步的話辆它,UserServiceOperationAspect只會是Spring容器中的一個(gè)bean誊薄,即使使用了@Aspect對它進(jìn)行了標(biāo)注履恩,但是它還沒有真正成為一個(gè)可用的切面,這些注解不會解析也不會創(chuàng)建將其轉(zhuǎn)換為切面的代理呢蔫,我們還需要做的一件事是:啟用AspectJ自動代理功能
如果你使用JavaConfigg的話切心,可以在配置類的類級別上通過使用@EnableAspectJAutoProxy注解啟動自動代理功能:
@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class AspectJConfig {
//聲明bean
//.....
}
如果你使用Spring的xml裝配bean,那么需要使用Spring AOP命名空間的<aop:aspectj-autoproxy />
元素
例如:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy />
<!--bean聲明略-->
</beans>
無論你使用JavaConfig還是XML片吊,AspectJ自動代理都會為使用@Aspect注解的bean創(chuàng)建一個(gè)代理绽昏,這個(gè)代理會圍繞著所有該切面的切點(diǎn)所匹配的bean。
值得注意的是俏脊,對于環(huán)繞通知全谤,它是最為強(qiáng)大的通知類型,它能夠讓你編寫的邏輯將被通知的目標(biāo)方法完全包裝起來爷贫,實(shí)際上就像在一個(gè)通知方法中同時(shí)編寫前置通知和后置通知认然。
另外,在使用環(huán)繞通知的時(shí)候漫萄,我們一般都會在通知的形參上增加一個(gè)ProceedingJoinPoint參數(shù)卷员,并且保證這個(gè)參數(shù)在第一個(gè)位置上,我們需要在通知中通過它來調(diào)用被通知的方法腾务,通知方法中我們可以做任何事情作為增強(qiáng)邏輯毕骡,當(dāng)要將控制權(quán)交給被通知的方法時(shí),需要調(diào)用ProceedingJoinPoint的peoceed()方法,如果不調(diào)用proceed()方法未巫,會阻塞對被通知方法的訪問窿撬。
4.3運(yùn)行測試觀察效果
我們啟動服務(wù),讓IUserService的getById()方法得以執(zhí)行叙凡,觀看效果:
我們將IUserService的實(shí)現(xiàn)類中增加一個(gè)int i = 2/0;
讓程序出現(xiàn)異常尤仍,觀察異常通知效果:
@Override
public User getById(Integer id) {
if (null != id && id > 0) {
int i = 2/0;
return userMapper.getById(id);
}
return null;
}
效果:
5.使用XML聲明切面
當(dāng)然我們也可以使用Xml聲明切面,我們再寫一個(gè)切面類XmlUserServiceOperationAspect狭姨,這次我們不使用注解的開發(fā)模式宰啦,具體代碼如下:
package com.tp.aop;
import com.tp.models.entity.User;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
/**
* FileName: XmlUserServiceOperationAspect
* Author: TP
* Description:基于XML形式聲明切面,切面測試
*/
public class XmlUserServiceOperationAspect {
private static final Logger log = LoggerFactory.getLogger(XmlUserServiceOperationAspect.class);
/**
* 測試service前置通知
*
* @param joinPoint 連接點(diǎn)信息
*/
public void getUserBefore(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
log.info(">>>>AOP-Service:前置通知饼拍,方法名:{}赡模,請求參數(shù):{}", methodName, Arrays.toString(args));
}
/**
* 測試service后置通知
*
* @param joinPoint 連接點(diǎn)信息
*/
public void getUserAfter(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
log.info(">>>>AOP-Service:后置通知,方法名:{}师抄,請求參數(shù):{}", methodName, Arrays.toString(args));
}
/**
* 測試service返回通知
*
* @param joinPoint 連接點(diǎn)信息
* @param user 返回值
*/
public void getUserAfterReturning(JoinPoint joinPoint, User user) {
Object[] args = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
log.info(">>>>AOP-Service:返回通知漓柑,方法名:{},請求參數(shù)id為{}叨吮,響應(yīng)結(jié)果User信息為:{}", methodName, Arrays.toString(args), user);
}
/**
* 測試service異常通知
*
* @param joinPoint 連接點(diǎn)信息
* @param e 異常信息
*/
public void getUserAfterTrowing(JoinPoint joinPoint, Exception e) {
Object[] args = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
log.info(">>>>AOP-Service:異常通知辆布,方法名:{},請求參數(shù)id為{}茶鉴,異常信息:{}", methodName, Arrays.toString(args), e);
}
/**
* 測試service環(huán)繞通知
* 需要注意的是锋玲,對于環(huán)繞通知必須給一個(gè)ProceedingJoinPoint參數(shù),并且放在參數(shù)的第一位
* 方法體中必須調(diào)用proceedingJoinPoint.proceed()方法涵叮,否則會阻塞目標(biāo)方法的執(zhí)行
*
* @param proceedingJoinPoint proceedingJoinPoint
* @return 返回值惭蹂,這里要注意:Around通知返回值不要寫成void(除非目標(biāo)方法返回值為void),否則最終返回結(jié)果為void
*/
public Object getUserAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object[] args = proceedingJoinPoint.getArgs();
String methodName = proceedingJoinPoint.getSignature().getName();
log.info(">>>>AOP-Service:環(huán)繞通知-開始割粮,方法名:{}盾碗,請求參數(shù)id為{}", methodName, Arrays.toString(args));
Object result = proceedingJoinPoint.proceed();
log.info(">>>>AOP-Service:環(huán)繞通知-結(jié)束,方法名:{}舀瓢,請求參數(shù)id為{}廷雅,返回結(jié)果為{}", methodName, Arrays.toString(args), result);
return result;
}
}
這時(shí)候我們就可以在Spring的XML配置文件中配置和聲明AOP:
<aop:aspectj-autoproxy />
<bean id="xmlUserServiceOperationAspect" class="com.tp.aop.XmlUserServiceOperationAspect"/>
<aop:config>
<!--聲明切點(diǎn)-->
<aop:pointcut id="pointCut" expression="execution(* com.tp.service.IUserService.getById(..))"/>
<!--聲明切面-->
<aop:aspect ref="xmlUserServiceOperationAspect">
<aop:before method="getUserBefore" pointcut-ref="pointCut"/>
<aop:after method="getUserAfter" pointcut-ref="pointCut"/>
<aop:after-returning method="getUserAfterReturning" pointcut-ref="pointCut" returning="user"/>
<aop:after-throwing method="getUserAfterTrowing" pointcut-ref="pointCut" throwing="e"/>
<aop:around method="getUserAround" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
編寫一個(gè)測試類:
public class AopMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("config/applicationContext.xml");
IUserService userService = (UserServiceImpl) ctx.getBean("userServiceImpl");
System.out.println(userService);
System.out.println("========================================================");
userService.getById(2);
}
}
執(zhí)行main函數(shù)效果如下:
6.如何在通知中獲取目標(biāo)方法信息
在日常開發(fā)中我們很可能會在通知的邏輯里需要知道目標(biāo)方法的信息,比如方法名京髓、參數(shù)信息航缀、方法的返回值等等...
訪問目標(biāo)方法最簡單的做法是定義增強(qiáng)處理方法時(shí),將第一個(gè)參數(shù)
定義為JoinPoint類型朵锣,當(dāng)該增強(qiáng)處理方法被調(diào)用時(shí)谬盐,該JoinPoint參數(shù)就代表了織入增強(qiáng)處理的連接點(diǎn)。
JoinPoint里包含了如下幾個(gè)常用的方法:
- Object[] getArgs:返回目標(biāo)方法的參數(shù)
- Signature getSignature:返回目標(biāo)方法的簽名
- Object getTarget:返回被織入增強(qiáng)處理的目標(biāo)對象
- Object getThis:返回AOP框架為目標(biāo)對象生成的代理對象
注意??:當(dāng)使用@Around處理時(shí)诚些,我們需要將第一個(gè)參數(shù)定義為ProceedingJoinPoint類型飞傀,該類是JoinPoint的子類皇型。
具體用法參見上面的XmlUserServiceOperationAspect.java
中的通知處理邏輯
另外如果只想訪問目標(biāo)方法的參數(shù),Spring還提供了一種更加簡潔的獲取參數(shù)的方法:我們可以在程序中使用args來綁定目標(biāo)方法的參數(shù)砸烦。如果在一個(gè)args表達(dá)式中指定了一個(gè)或多個(gè)參數(shù)弃鸦,該切入點(diǎn)將只匹配具有對應(yīng)形參的方法,且目標(biāo)方法的參數(shù)值將被傳入增強(qiáng)處理方法幢痘。
例如:
@Aspect
public class AccessArgAdviceTest {
@AfterReturning(
pointcut="execution(* com.tp.service.*.access*(..)) && args(time, name)",
returning="returnValue")
public void access(Date time, String name, Object returnValue) {
System.out.println("目標(biāo)方法中的參數(shù)String = " + name);
System.out.println("目標(biāo)方法中的參數(shù)Date = " + time);
System.out.println("目標(biāo)方法的返回結(jié)果returnValue = " + returnValue);
}
}
上面的程序中唬格,定義pointcut時(shí),表達(dá)式中增加了args(time, name)部分颜说,意味著可以在增強(qiáng)處理方法(即access方法)中定義time和name兩個(gè)屬性——這兩個(gè)形參的類型可以隨意指定购岗,但一旦指定了這兩個(gè)參數(shù)的類型,則這兩個(gè)形參類型將用于限制該切入點(diǎn)只匹配第一個(gè)參數(shù)類型為Date门粪,第二個(gè)參數(shù)類型為name的方法(方法參數(shù)個(gè)數(shù)和類型若有不同均不匹配)喊积。
注意,在定義returning的時(shí)候玄妈,這個(gè)值(即上面的returning="returnValue"中的returnValue)作為增強(qiáng)處理方法的形參時(shí)乾吻,位置可以隨意,即:如果上面access方法的簽名可以為:
public void access(Date time, Object returnValue, String name)
也可以為:
public void access(Object returnValue, Date time, String name)
還可以為:
public void access(Date time, String name, Object returnValue)
只需要滿足另外的參數(shù)名的順序和pointcut中args(param1, param2)的順序相同即可拟蜻。
除此之外绎签,使用args表達(dá)式時(shí),還可以使用如下形式:args(param1, param2, ..)
注意args參數(shù)中后面的兩個(gè)點(diǎn)酝锅,它表示可以匹配更多參數(shù)诡必。
在例子args(param1, param2, ..)中,表示目標(biāo)方法只需匹配前面param1和param2的類型即可
我們上面的基于注解創(chuàng)建切面的例子UserServiceOperationAspect.java
就是使用這種方式獲取目標(biāo)方法參數(shù)的
7.通知及切面的執(zhí)行順序
5種通知的執(zhí)行順序
不同切面同一個(gè)切點(diǎn)時(shí)的執(zhí)行順序
Spring AOP采用和AspectJ一樣的有限順序來織入增強(qiáng)處理:
- 在“進(jìn)入”連接點(diǎn)時(shí)屈张,最高優(yōu)先級的增強(qiáng)處理將先被織入(所以給定的兩個(gè)Before增強(qiáng)處理中擒权,優(yōu)先級高的那個(gè)會先執(zhí)行)
- 在“退出”連接點(diǎn)時(shí),最高優(yōu)先級的增強(qiáng)處理會最后被織入(所以給定的兩個(gè)After增強(qiáng)處理中阁谆,優(yōu)先級高的那個(gè)會后執(zhí)行)。
當(dāng)不同的切面中的多個(gè)增強(qiáng)處理需要在同一個(gè)連接點(diǎn)被織入時(shí)愉老,Spring AOP將以隨機(jī)的順序來織入這些增強(qiáng)處理场绿。如果應(yīng)用需要指定不同切面類里的增強(qiáng)處理的優(yōu)先級,Spring提供了如下兩種解決方案:
- 讓切面類實(shí)現(xiàn)org.springframework.core.Ordered接口:實(shí)現(xiàn)該接口只需要實(shí)現(xiàn)一個(gè)int getOrder()方法嫉入,該方法返回值越小焰盗,優(yōu)先級越高
- 直接使用@Order注解來修飾一個(gè)切面類:使用這個(gè)注解時(shí)可以配置一個(gè)int類型的value屬性,該屬性值越小咒林,優(yōu)先級越高
例如xml中:
<aop:aspect ref="xmlUserServiceOperationAspect" order="2">
<!--聲明通知-->
<aop:before method="getUserBefore" pointcut-ref="pointCut"/>
<aop:after method="getUserAfter" pointcut-ref="pointCut"/>
<aop:after-returning method="getUserAfterReturning" pointcut-ref="pointCut" returning="user"/>
<aop:after-throwing method="getUserAfterTrowing" pointcut-ref="pointCut" throwing="e"/>
<aop:around method="getUserAround" pointcut-ref="pointCut"/>
</aop:aspect>
基于注解的切面:
@Aspect
@Order(1)
@Component
public class UserServiceOperationAspect {
....
}
將2種切面同時(shí)生效熬拒,運(yùn)行測試類后效果如下: