前置通知[Before advice] :在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。
正常返回通知[After returning advice] :在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。
异常返回通知[After throwing advice] :在连接点抛出异常后执行。
返回通知[After (finally) advice] :在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
环绕通知[Around advice]:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。
以上是查阅文档时别人文档给出的解释,接下来我们实践一下,最后看看效果的差别。
前置通知和返回通知已经在上一篇看过了,我们这里测试around
@Around("execution(* com.hjj.source.math.math1.*(..))")
public double around(ProceedingJoinPoint jp){
try{
System.out.println("--------around begin--------");
jp.proceed();
System.out.println("---------around end---------");
}catch(Throwable e){
e.printStackTrace();
}
return 0;
}
同样,注释Around,使用环绕通知需要注意亮点,1:环绕开始后需要执行一次proceed,不然此方法阻塞,程序无法执行。
2.环绕通知的返回类型,必须与切点的返回类型一致,不然会报错,就像这样
Null return value from advice does not match primitive return type for: public double com.hjj.source.math.math1.add(double,double)
我这里修改around的返回类型为double,在实际过程中这一点需要考虑到。
--------around begin--------
----------运算开始----------
add
12.34+23.45=35.79
---------around end---------
----------运算结束----------
这里可以看到执行的顺序为around-before-around-after。至于其他两个是针对程序运行的正常与否进行检查的。
这里上代码
@AfterReturning("execution(* com.hjj.source.math.math1.*(..))")
public void aftereturing(JoinPoint jp){
System.out.println("----------正常----------");
}
@AfterThrowing(throwing = "ex",pointcut = "execution(* com.hjj.source.math.math1.*(..))")
public void afterthrowing(Throwable ex){
System.out.println("----------异常----------");
System.out.println(ex);
}
接下来,改写一下源代码,抛出一个zero异常
//除
public double div(double n1,double n2){
double result=n1/n2;
System.out.println(n1+"/"+n2+"="+result);
if (n2==0) throw new IllegalArgumentException("除数不能为0");
return result;
}
然后看一下结果,了解了afterruning和afterthrowing的区别。(只会执行其中一个,而且一定会执行一个,多数用在日志记录里)。
----------运算开始----------
mut
12.34X0.0=0.0
----------运算结束----------
----------正常----------
----------运算开始----------
div
12.34/0.0=Infinity
----------运算结束----------
----------异常----------
java.lang.IllegalArgumentException: 除数不能为0
因为能力有限,只了解了Spring中AOP的部分,利用Aspectj进行注解进行配置,而AspectJ还有许多强大的功能和复杂的语法,这里就不深入了解了,最后了解一下,切入点的多种表达形式,在实际运用过程中可以再深入学习一下,这里先mark。
Spring AOP支持的切入点指示符:
(1)execution:用来匹配执行方法的连接点
A:@Pointcut("execution(* com.aijava.springcode.service..*.*(..))")
第一个*表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个*表示所有类,第三个*表示所有方法,第二个..表示
方法的任意参数个数
B:@Pointcut("within(com.aijava.springcode.service.*)")
within限定匹配方法的连接点,上面的就是表示匹配service包下的任意连接点
C:@Pointcut("this(com.aijava.springcode.service.UserService)")
this用来限定AOP代理必须是指定类型的实例,如上,指定了一个特定的实例,就是UserService
D:@Pointcut("bean(userService)")
bean也是非常常用的,bean可以指定IOC容器中的bean的名称