[Spring Framework]AOP配置管理③(AOP通知获取数据)
AOP通知获取数据
目前我们写AOP仅仅是在原始方法前后追加一些操作,接下来我们要说说AOP中数据相关的内容,我们将从以下三个方面来研究切入点的相关信息:
获取参数
获取返回值
获取异常
前面我们介绍通知类型的时候总共讲了五种,那么对于这五种类型都会有参数,返回值和异常吗?
我们先来一个个分析下:
- 获取切入点方法的参数,所有的通知类型都可以获取参数
JoinPoint
:适用于前置、后置、返回后、抛出异常后通知ProceedingJoinPoint
:适用于环绕通知
- 获取切入点方法返回值,前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究,我们主要讨论:
- 返回后通知
- 环绕通知
- 获取切入点方法运行异常信息,前置和返回后通知是不会有,后置通知可有可无,所以不做研究,我们只讨论:
- 抛出异常后通知
- 环绕通知
项目环境
首先我们准备,一个接口以及其实现类:
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 "CSDN";
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
通知类:
@Component
@Aspect
public class MyAdvice2 {
@Pointcut("execution(* *BookDao4.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 ...");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
然后我们的测试demo:
public class App12 {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao4 bookDao = ctx.getBean(BookDao4.class);
String name = bookDao.findName(100);
System.out.println(name);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
获取切入点方法的参数
因为所有的通知类型都可以获取切入点方法的参数,所以这里我们分为两种来讨论:
- 非环绕通知获取方式
- 环绕通知获取方式
非环绕通知获取方式
核心操作:
在方法上添加JoinPoint,通过JoinPoint来获取参数
@Component
@Aspect
public class MyAdvice2 {
@Pointcut("execution(* *.BookDao4.findName(..))")
private void pt(){}
@Before("pt()")
public void before(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("before advice ..." );
}
//...其他的略
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
结果:
这里使用一个Object数组是因为参数个数不确定,参数类型也不确定,我们再在原切入点方法中添加一个参数看看效果:
运行结果:
说明:
使用JoinPoint的方式获取参数适用于前置
、后置
、返回后
、抛出异常后
通知。
环绕通知获取方式
环绕通知使用的是ProceedingJoinPoint,因为ProceedingJoinPoint是JoinPoint类的子类,所以对于ProceedingJoinPoint类中应该也会有对应的getArgs()
方法,我们去验证下:
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* *.BookDao4.findName(..))")
private void pt(){}
@Around("pt()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
Object[] args = proceedingJoinPoint.getArgs();
Object proceed = proceedingJoinPoint.proceed();
System.out.println("around advice ...");
System.out.println(Arrays.toString(args));
return proceed;
}
//其他的略
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
执行结果:
注意:
-
proceedingJoinPoint.proceed()方法是有两个构造方法,分别是:
-
调用无参数的proceed,当原始方法有参数,会在调用的过程中自动传入参数
-
所以调用这两个方法的任意一个都可以完成功能
-
但是当需要修改原始方法的参数时,就只能采用带有参数的方法,如下:
@Component @Aspect public class MyAdvice { @Pointcut("execution(* *.BookDao4.findName(..))") private void pt(){} @Around("pt()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ Object[] args = proceedingJoinPoint.getArgs(); args[0] = 666; Object proceed = proceedingJoinPoint.proceed(args); System.out.println("around advice ..."); System.out.println(Arrays.toString(args)); return proceed; } //其他的略 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
结果:
有了这个特性后,我们就可以在环绕通知中对原始方法的参数进行拦截过滤,避免由于参数的问题导致程序无法正确运行,保证代码的健壮性。
-
获取返回值
对于返回值,只有返回后AfterReturing
和环绕Around
这两个通知类型可以获取,具体如何获取?
环绕通知获取返回值
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* *.BookDao4.findName(..))")
private void pt(){}
@Around("pt()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
Object[] args = proceedingJoinPoint.getArgs();
args[0] = 666;
Object proceed = proceedingJoinPoint.proceed(args);
System.out.println("around advice ...");
System.out.println(Arrays.toString(args));
return proceed;
}
//其他的略
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
上述代码中,proceed
就是方法的返回值,我们是可以直接获取,不但可以获取,如果需要还可以进行修改。
返回后通知获取返回值
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* *.BookDao4.findName(..))")
private void pt(){}
@AfterReturning(value = "pt()",returning = "str")
public void afterReturning(Object str) {
System.out.println(str);
System.out.println("afterReturning advice ...");
}
//其他的略
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
结果;
注意:
(1)参数名的问题
(2)afterReturning方法参数类型的问题
参数类型可以写成String,但是为了能匹配更多的参数类型,建议写成Object类型
(3)afterReturning方法参数的顺序问题
获取异常
对于获取抛出的异常,只有抛出异常后AfterThrowing
和环绕Around
这两个通知类型可以获取,具体如何获取?
环绕通知获取异常
这块比较简单,以前我们是抛出异常,现在只需要将异常捕获,就可以获取到原始方法的异常信息了
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* *.BookDao4.findName(..))")
private void pt(){}
@Around("pt()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
Object[] args = proceedingJoinPoint.getArgs();
args[0] = 666;
Object proceed = null;
try {
proceed = proceedingJoinPoint.proceed(args);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("around advice ...");
System.out.println(Arrays.toString(args));
return proceed;
}
//其他的略
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
在catch方法中就可以获取到异常,至于获取到异常以后该如何处理,这个就和你的业务需求有关了。
抛出异常后通知获取异常
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* *.BookDao4.findName(..))")
private void pt(){}
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {
System.out.println(t);
System.out.println("afterThrowing advice ...");
}
//其他的略
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
如何让原始方法抛出异常,方式有很多:
最后的结果:
注意:
文章来源: blog.csdn.net,作者:十八岁讨厌编程,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/zyb18507175502/article/details/125825493
- 点赞
- 收藏
- 关注作者
评论(0)