Spring AOP通知參數(shù)
前邊章節(jié)已經(jīng)介紹了聲明通知践剂,但如果想獲取被被通知方法參數(shù)并傳遞給通知方法,該如何實現(xiàn)呢引有?接下來我們將介紹兩種獲取通知參數(shù)的方式儒鹿。
1. 使用JoinPoint獲取
Spring AOP提供使用org.aspectj.lang.JoinPoint類型獲取連接點數(shù)據(jù),任何通知方法的第一個參數(shù)都可以是JoinPoint(環(huán)繞通知是ProceedingJoinPoint凳忙,JoinPoint子類)业踏,當然第一個參數(shù)位置也可以是JoinPoint.StaticPart類型,這個只返回連接點的靜態(tài)部分涧卵。
-
JoinPoint
:提供訪問當前被通知方法的目標對象勤家、代理對象、方法參數(shù)等數(shù)據(jù):public interface JoinPoint { String toString(); //連接點所在位置的相關(guān)信息 String toShortString(); //連接點所在位置的簡短相關(guān)信息 String toLongString(); //連接點所在位置的全部相關(guān)信息 Object getThis(); //返回AOP代理對象,如果想要使用這個方法的話柳恐,最好使用this連接點伐脖,這樣可以獲得最佳的性能。 Object getTarget(); //返回目標對象,如果想要使用這個方法的話乐设,最好使用target連接點讼庇,這樣可以獲得最佳的性能。 Object[] getArgs(); //返回被通知方法參數(shù)列表 Signature getSignature(); //返回當前連接點簽名 SourceLocation getSourceLocation(); //返回連接點方法所在類文件中的位置 String getKind(); //連接點類型 StaticPart getStaticPart(); //返回連接點靜態(tài)部分 // getKind 方法的返回值 static String METHOD_EXECUTION = "method-execution"; static String METHOD_CALL = "method-call"; static String CONSTRUCTOR_EXECUTION = "constructor-execution"; static String CONSTRUCTOR_CALL = "constructor-call"; static String FIELD_GET = "field-get"; static String FIELD_SET = "field-set"; static String STATICINITIALIZATION = "staticinitialization"; static String PREINITIALIZATION = "preinitialization"; static String INITIALIZATION = "initialization"; static String EXCEPTION_HANDLER = "exception-handler"; static String SYNCHRONIZATION_LOCK = "lock"; static String SYNCHRONIZATION_UNLOCK = "unlock"; static String ADVICE_EXECUTION = "adviceexecution"; }
ProceedingJoinPoint
:用于環(huán)繞通知近尚,使用proceed()
方法來執(zhí)行目標方法:
public interface ProceedingJoinPoint extends JoinPoint {
void set$AroundClosure(AroundClosure arc); // 這是個內(nèi)部方法蠕啄,不應該直接調(diào)用
public Object proceed() throws Throwable; // 執(zhí)行目標函數(shù),以默認的參數(shù)執(zhí)行
public Object proceed(Object[] args) throws Throwable; // 執(zhí)行目標函數(shù)戈锻,并傳入所需的參數(shù)
}
-
JoinPoint.StaticPart
:提供訪問連接點的靜態(tài)部分歼跟,如被通知方法簽名却嗡、連接點類型等:
public interface StaticPart {
Signature getSignature(); // 返回當前連接點簽名
SourceLocation getSourceLocation(); // 返回連接點所在資源路徑
String getKind(); // 連接點類型
int getId(); // 唯一標識
String toString(); // 連接點所在位置的相關(guān)信息
String toShortString(); // 連接點所在位置的簡短相關(guān)信息
String toLongString(); // 連接點所在位置的全部相關(guān)信息
}
如果使用該種方式聲明參數(shù),必須放到方法第一個位置上嘹承,如
@Aspect
@Component
public class JoinPointAop {
// 切點范圍
@Pointcut("execution(* com.learn.service..IJoinPointService+.*(..))")
public void pointcut(){ }
@Before("pointcut()")
public void before1(JoinPoint jp) {
System.out.println("---------------@Before1----------------");
System.out.println(Arrays.toString(jp.getArgs()));
}
@Before("pointcut()")
public void before2(JoinPoint.StaticPart jp) {
System.out.println("---------------@Before2----------------");
System.out.println(jp.getSignature());
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("---------------@Around----------------");
return pjp.proceed();
}
}
2. 手動指定
通過切入點表達式可以將相應的參數(shù)自動傳遞給通知方法。
在Spring AOP中如庭,除了execution和bean指示符不能傳遞參數(shù)給通知方法叹卷,其他指示符都可以將匹配的相應參數(shù)或?qū)ο笞詣觽鬟f給通知方法。
簡單使用
例:
@Before(value="execution(* com.learn.service..IAppointService+.say(*)) && args(param)", argNames="param") //明確指定了
public void before1(String param) {
System.out.println("===param:" + param);
}
切入點表達式execution( com.learn.service..IAppointService+.say()) && args(param)**:
- 首先execution(* com.learn.service..IAppointService+.say(*))匹配IAppointService接口的實現(xiàn)類的say方法坪它,且有一個任何類型的參數(shù)骤竹;
- args(param)將首先查找通知方法上同名的參數(shù),并在方法執(zhí)行時(運行時)匹配傳入的參數(shù)是使用該同名參數(shù)類型往毡,即java.lang.String蒙揣;如果匹配將把該被通知參數(shù)傳遞給通知方法上同名參數(shù)。
其中argNames可以省略不寫开瞭,但是省略的話如果在class文件中沒生成變量調(diào)試信息是獲取不到方法參數(shù)名字的懒震。 - 如果想使用JoinPoint當做參數(shù)的話,也需要在argNames中指定嗤详,如:
@Before(value="execution(* com.learn.service..IAppointService+.say(*)) && args(param)", argNames="jp, param") //明確指定了 public void before1(JoinPoint jp, String param) { System.out.println("===JoinPoint:" + jp.getKind()); System.out.println("===param:" + param); }
組合使用
@Before(value = "pointcut() && args(param) && this(service) && @annotation(secure)", argNames = " jp, param, service, secure")
public void before2(JoinPoint jp, String param, IAppointService service, Secure secure) {
service.logInfo("==before==");
System.out.println("===JoinPoint:" + jp.getKind());
System.out.println("===param:" + param);
System.out.println("===secure:" + secure.value());
}
也可以使用引用切入點的方式獲取參數(shù):
@Pointcut(value = "args(param)", argNames = "param")
private void pointcut1(String param) {
}
@Pointcut(value = "@annotation(secure)", argNames = "secure")
private void pointcut2(Secure secure) {
}
@Before(value = "pointcut1(param) && pointcut2(secure)", argNames = "jp, param, secure")
public void before3(JoinPoint jp, String param, Secure secure) {
System.out.println("before3");
}