Spring AOP通知參數
前邊章節已經介紹了聲明通知,但如果想獲取被被通知方法參數并傳遞給通知方法,該如何實現呢?接下來我們將介紹兩種獲取通知參數的方式。
1. 使用JoinPoint獲取
Spring AOP提供使用org.aspectj.lang.JoinPoint類型獲取連接點數據,任何通知方法的第一個參數都可以是JoinPoint(環繞通知是ProceedingJoinPoint,JoinPoint子類),當然第一個參數位置也可以是JoinPoint.StaticPart類型,這個只返回連接點的靜態部分。
-
JoinPoint
:提供訪問當前被通知方法的目標對象、代理對象、方法參數等數據:public interface JoinPoint { String toString(); //連接點所在位置的相關信息 String toShortString(); //連接點所在位置的簡短相關信息 String toLongString(); //連接點所在位置的全部相關信息 Object getThis(); //返回AOP代理對象,如果想要使用這個方法的話,最好使用this連接點,這樣可以獲得最佳的性能。 Object getTarget(); //返回目標對象,如果想要使用這個方法的話,最好使用target連接點,這樣可以獲得最佳的性能。 Object[] getArgs(); //返回被通知方法參數列表 Signature getSignature(); //返回當前連接點簽名 SourceLocation getSourceLocation(); //返回連接點方法所在類文件中的位置 String getKind(); //連接點類型 StaticPart getStaticPart(); //返回連接點靜態部分 // 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
:用于環繞通知,使用proceed()
方法來執行目標方法:
public interface ProceedingJoinPoint extends JoinPoint {
void set$AroundClosure(AroundClosure arc); // 這是個內部方法,不應該直接調用
public Object proceed() throws Throwable; // 執行目標函數,以默認的參數執行
public Object proceed(Object[] args) throws Throwable; // 執行目標函數,并傳入所需的參數
}
-
JoinPoint.StaticPart
:提供訪問連接點的靜態部分,如被通知方法簽名、連接點類型等:
public interface StaticPart {
Signature getSignature(); // 返回當前連接點簽名
SourceLocation getSourceLocation(); // 返回連接點所在資源路徑
String getKind(); // 連接點類型
int getId(); // 唯一標識
String toString(); // 連接點所在位置的相關信息
String toShortString(); // 連接點所在位置的簡短相關信息
String toLongString(); // 連接點所在位置的全部相關信息
}
如果使用該種方式聲明參數,必須放到方法第一個位置上,如
@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. 手動指定
通過切入點表達式可以將相應的參數自動傳遞給通知方法。
在Spring AOP中,除了execution和bean指示符不能傳遞參數給通知方法,其他指示符都可以將匹配的相應參數或對象自動傳遞給通知方法。
簡單使用
例:
@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接口的實現類的say方法,且有一個任何類型的參數;
- args(param)將首先查找通知方法上同名的參數,并在方法執行時(運行時)匹配傳入的參數是使用該同名參數類型,即java.lang.String;如果匹配將把該被通知參數傳遞給通知方法上同名參數。
其中argNames可以省略不寫,但是省略的話如果在class文件中沒生成變量調試信息是獲取不到方法參數名字的。 - 如果想使用JoinPoint當做參數的話,也需要在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());
}
也可以使用引用切入點的方式獲取參數:
@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");
}