一、通知概述
(1)在具体的连接点上要执行的操作;
(2)一个切面可以包括一个或者多个通知;
(3)通知所使用的注解的值往往是切入点表达式;
二、前置通知
(1)前置通知:在方法执行之前执行的通知;
(2)使用 @Before 注解
/**
* @Before:将方法指定为前置通知
* 必须设置 value,其值为 切入点表达式
* 前置通知:作用于方法执行之前
*/
@Before(value = "execution(public int com.spring.aop.ICalc.add(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法执行之前");
Object[] args = joinPoint.getArgs(); //获取方法的参数
String methodName = joinPoint.getSignature().getName(); //获取方法名
System.out.println("方法名:" + methodName + ",参数:" + Arrays.toString(args));
}
三、后置通知
(1)后置通知:后置通知是在连接点完成之后执行的,即连接点返回结果或者抛出异常的时候;
(2)使用 @After 注解;
/**
* @After:将方法标注为后置通知
* 后置通知:作用于方法的 finally 语句块中,即不管有没有异常都会执行
* @param joinPoint
*/
@After(value = "execution(public int com.spring.aop.ICalc.*(int, int))")
public void afterMethod(JoinPoint joinPoint) {
System.out.println("方法执行之后");
Object[] args = joinPoint.getArgs(); //获取方法的参数
String methodName = joinPoint.getSignature().getName(); //获取方法名
System.out.println("方法名:" + methodName + ",参数:" + Arrays.toString(args));
}
四、返回通知
(1)返回通知:无论连接点是正常返回还是抛出异常,后置通知都会执行。如果只想在连接点返回的时候记录日志,应使用返回通知代替后置通知;
(2)使用 @AfterReturning 注解,在返回通知中访问连接点的返回值;
① 在返回通知中,只要将 returning 属性添加到 @AfterReturning 注解中,就可以访问连接点的返回值,该属性的值即为用来传入返回值的参数名称;
returning属性:告诉Spring使用什么变量来接结果返回值
② 必须在通知方法的签名中添加一个同名参数,在运行时 Spring AOP 会通过这个参数传递返回值;
③ 原始的切点表达式需要出现在 pointcut 属性中;
/**
* @AfterReturning:将方法标注为返回通知;
* 返回通知:作用于方法执行之后,如果发生了异常,就不再执行
* 可通过 returning 设置接收方法返回值的变量名
* 要想在方法中使用,必须在方法的形参中设置和变量名相同的参数名的参数
* @param joinPoint
* @param result
*/
@AfterReturning(value = "execution(public int com.spring.aop.ICalc.*(int, int))", returning="result")
public void afterReturningMethod(JoinPoint joinPoint, Object result) {
System.out.println("方法返回通知");
Object[] args = joinPoint.getArgs(); //获取方法的参数
String methodName = joinPoint.getSignature().getName(); //获取方法名
System.out.println("方法名:" + methodName + ",参数:" + Arrays.toString(args));
System.out.println("返回值:"+result);
}
五、异常通知
(1)异常通知:只在连接点抛出异常时才执行异常通知;
(2)将 throwing 属性添加到 @AfterThrowing 注解中,也可以访问连接点抛出的异常。
throwing:告诉Spring使用什么变量来接结果异常信息
Throwable 是所有错误和异常类的顶级父类,所以在异常通知方法可以捕获到任何错误和异常。
(3)如果只对某种特殊的异常类型感兴趣,可以将参数声明为其他异常的参数类型,然后通知就只在抛出这个类型及其子类的异常时才被执行;
/**
* @AfterThrowing:将方法标注为异常通知(例外通知)
* 异常通知(例外通知):作用于方法抛出异常时
* 可通过throwing设置接收方法返回的异常信息
* 在参数列表中可通过具体的异常类型,来对指定的异常信息进行操作
* @param ex
*/
@AfterThrowing(value = "execution(public int com.spring.aop.ICalc.*(int, int))",throwing="ex")
public void afterThrowingMethod(Exception ex) {
System.out.println("有异常了。");
System.out.println("message:" + ex);
}
public void afterThrowingMethod(ArithmeticException ex) { //可以指定异常的类型,根据不同类型的异常,选择不同的操作
System.out.println("有异常了。");
System.out.println("message:" + ex);
}
六、Spring对通知方法的约束
Spring对通知方法的要求不严格:修改返回值和去掉静态static都照样运行
但唯一有要求的是方法的参数列表一定不能乱写 原因:通知方法是Spring利用反射调用的,每次调用方法都需要确定这个方法的参数表的值; 参数表上的每一个参数,Spring都得知道是什么。
举例:
@After("execution(public int com.achang.inter.impl.MyMathCalculator.*(int,int))")
public int logEnd(JoinPoint joinPoint){
String name = joinPoint.getSignature().getName();
System.out.println("【" + name + "】方法执行最终完成");
return 0;
}
对于有异常和返回值的方法,指定的异常、结果返回值一定要写大:
public static void logException(JoinPoint joinPoint,Exception e,Object result){
}
//Exception e, Object result
不然他只接收,你写的异常和返回值类型如:只接受空指针异常和Integer类型
NullPointerException e
Integer result
七、通知方法的执行顺序
通知方法的执行顺序:
正常执行情况:@Before(前置通知)—>@After(后置通知)—>@AfterReturning(正常返回)
异常执行情况:@Before(前置通知)—>@After(后置通知)—>@AfterThrowing(方法异常)