前面的部落格我們寫AOP僅僅是在原始方法前後追加一些操作,接下來我們要說說AOP中資料相關的內容,我們將從獲取引數
、獲取返回值
和獲取異常
前面我們介紹通知型別的時候總共講了五種,那麼對於這五種型別都會有引數,返回值和異常嗎?
我們先來一個個分析下:
獲取切入點方法的引數,所有的通知型別都可以獲取引數
JoinPoint:適用於前置、後置、返回後、丟擲異常後通知
ProceedingJoinPoint:適用於環繞通知
獲取切入點方法返回值,前置和丟擲異常後通知是沒有返回值,後置通知可有可無,所以不做研究
返回後通知
環繞通知
獲取切入點方法執行異常資訊,前置和返回後通知是不會有,後置通知可有可無,所以不做研究
丟擲異常後通知
環繞通知
建立一個Maven專案
pom.xml新增Spring依賴
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
新增BookDao和BookDaoImpl類
public interface BookDao {
public String findName(int id);
}
@Repository
public class BookDaoImpl implements BookDao {
public String findName(int id) {
System.out.println("id:"+id);
return "itcast";
}
}
建立Spring的設定類
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}
編寫通知類
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
private void pt(){}
@Before("pt()")
public void before() {
System.out.println("before advice ..." );
}
@After("pt()")
public void after() {
System.out.println("after advice ...");
}
@Around("pt()")
public Object around() throws Throwable{
Object ret = pjp.proceed();
return ret;
}
@AfterReturning("pt()")
public void afterReturning() {
System.out.println("afterReturning advice ...");
}
@AfterThrowing("pt()")
public void afterThrowing() {
System.out.println("afterThrowing advice ...");
}
}
編寫App執行類
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
String name = bookDao.findName(100);
System.out.println(name);
}
}
最終建立好的專案結構如下:
在方法上新增JoinPoint,通過JoinPoint來獲取引數
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
private void pt(){}
@Before("pt()")
public void before(JoinPoint jp)
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("before advice ..." );
}
//...其他的略
}
執行App類,可以獲取如下內容,說明引數100已經被獲取
思考:方法的引數只有一個,為什麼獲取的是一個陣列?
因為引數的個數是不固定的,所以使用陣列更通配些。
如果將引數改成兩個會是什麼效果呢?
(1)修改BookDao介面和BookDaoImpl實現類
public interface BookDao {
public String findName(int id,String password);
}
@Repository
public class BookDaoImpl implements BookDao {
public String findName(int id,String password) {
System.out.println("id:"+id);
return "itcast";
}
}
(2)修改App類,呼叫方法傳入多個引數
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
String name = bookDao.findName(100,"itheima");
System.out.println(name);
}
}
(3)執行App,檢視結果,說明兩個引數都已經被獲取到
說明:
使用JoinPoint的方式獲取引數適用於前置
、後置
、返回後
、丟擲異常後
通知。
環繞通知使用的是ProceedingJoinPoint,因為ProceedingJoinPoint是JoinPoint類的子類,所以對於ProceedingJoinPoint類中應該也會有對應的getArgs()
方法,我們去驗證下:
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
private void pt(){}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp)throws Throwable {
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
Object ret = pjp.proceed();
return ret;
}
//其他的略
}
執行App後檢視執行結果,說明ProceedingJoinPoint也是可以通過getArgs()獲取引數
注意:
pjp.proceed()方法是有兩個構造方法,分別是:
呼叫無引數的proceed,當原始方法有引數,會在呼叫的過程中自動傳入引數
所以呼叫這兩個方法的任意一個都可以完成功能
但是當需要修改原始方法的引數時,就只能採用帶有引數的方法,如下:
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
private void pt(){}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
args[0] = 666;
Object ret = pjp.proceed(args);
return ret;
}
//其他的略
}
有了這個特性後,我們就可以在環繞通知中對原始方法的引數進行攔截過濾,避免由於引數的問題導致程式無法正確執行,保證程式碼的健壯性。
對於返回值,只有返回後AfterReturing
和環繞Around
這兩個通知型別可以獲取,具體如何獲取?
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
private void pt(){}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
args[0] = 666;
Object ret = pjp.proceed(args);
return ret;
}
//其他的略
}
上述程式碼中,ret
就是方法的返回值,我們是可以直接獲取,不但可以獲取,如果需要還可以進行修改。
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
private void pt(){}
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(Object ret) {
System.out.println("afterReturning advice ..."+ret);
}
//其他的略
}
注意:
(1)引數名的問題
(2)afterReturning方法引數型別的問題
引數型別可以寫成String,但是為了能匹配更多的引數型別,建議寫成Object型別
(3)afterReturning方法引數的順序問題
執行App後檢視執行結果,說明返回值已經被獲取到
對於獲取丟擲的異常,只有丟擲異常後AfterThrowing
和環繞Around
這兩個通知型別可以獲取,具體如何獲取?
這塊比較簡單,以前我們是丟擲異常,現在只需要將異常捕獲,就可以獲取到原始方法的異常資訊了
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
private void pt(){}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp){
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
args[0] = 666;
Object ret = null;
try{
ret = pjp.proceed(args);
}catch(Throwable throwable){
t.printStackTrace();
}
return ret;
}
//其他的略
}
在catch方法中就可以獲取到異常,至於獲取到異常以後該如何處理,這個就和你的業務需求有關了。
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
private void pt(){}
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {
System.out.println("afterThrowing advice ..."+t);
}
//其他的略
}
如何讓原始方法丟擲異常,方式有很多,
@Repository
public class BookDaoImpl implements BookDao {
public String findName(int id,String password) {
System.out.println("id:"+id);
if(true){
throw new NullPointerException();
}
return "itcast";
}
}
注意:
執行App後,檢視控制檯,就能看的異常資訊被列印到控制檯
本文來自部落格園,作者:|舊市拾荒|,轉載請註明原文連結:https://www.cnblogs.com/xiaoyh/p/16412329.html