AOP通知中获取数据
之前我们写AOP仅仅是在原始方法前后追加一些操作,接下来我们要说说AOP中数据相关的内容,我们将从获取参数、获取返回值和获取异常三个方面来研究切入点的相关信息。
- 获取切入点方法的参数:所有的通知类型都可以获取参数
- JoinPoint:适用于前置、后置、返回后、抛出异常后通知
- ProceedingJoinPoint:适用于环绕通知
- 获取切入点方法返回值:前置和抛出异常后通知是没有返回值,后置通知可有可无。
- 返回后通知
- 环绕通知
- 获取切入点方法运行异常信息:前置和返回后通知不会有,后置通知可有可无。
- 抛出异常后通知
- 环绕通知
参数获取
非环绕通知参数获取方式:
在方法上添加JoinPoint,通过JoinPoint来获取参数
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.taro.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 ..." );
}
//...其他的略
}
如果运行我们会发现返回值是用一个数组呈现的,因为参数的个数是不固定的,所以使用数组更通配些。
环绕通知参数获取方式
环绕通知使用的是ProceedingJoinPoint,上一篇Spring AOP通知类型里面也见过,因为ProceedingJoinPoint是JoinPoint类的子类,所以对于ProceedingJoinPoint类中应该也会有对应的getArgs()
方法。
🍊个🌰:
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.taro.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;
}
}
- 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.taro.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.taro.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方法参数的顺序问题
异常获取
对于获取抛出的异常,只有抛出异常后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";
}
}
注意: