前面简单的介绍了一下@Before注解的使用,其实AspectJ支持5种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterReturning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
1.后置通知:
后置通知是在连接点完成之后执行的, 即连接点返回结果或者抛出异常的时候, 后置通知中的方法都会执行。
下面写一个后置通知的方法:
/**后置通知
* 在后置通知中还不能访问目标
* 方法执行的 结果
* */
@After("execution(* com.spring.aop.*.*(int,int))")
public void afterMethod(JoinPoint joinPoint){
String methodname=joinPoint.getSignature().getName();
System.out.println("The method "+methodname+" ends");
}
具体的配置可以参考前面一篇博文,这里不再赘述。
在主程序中制造一个异常:
public class Main {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
AtitheticCalculator atitheticCalculator=ctx.getBean(AtitheticCalculator.class);
int result=atitheticCalculator.div(3,0);
System.out.println(result);
}
}
后置通知还是得到了运行。
2.返回通知:
/**
* 在方法结束后
* 受执行的代码
* 返回通知是可以访问到方法的返回值的
* */
@AfterReturning(value="execution(public int com.spring.aop.AtitheticCalculator.*(..))",returning = "result")
public void afterReturing(JoinPoint joinPoint,Object result){
String methodName=joinPoint.getSignature().getName();
System.out.println("The method "+methodName+" ends with "+result);
}
public class Main {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
AtitheticCalculator atitheticCalculator=ctx.getBean(AtitheticCalculator.class);
int result=atitheticCalculator.div(9,3);
System.out.println(result);
}
}
执行之
/**
* 异常通知
* 在方法出现异常时会执行的代码
* 而且可以访问到异常对象
* 且可以指定出现特定异常是再执行特定的代码
* */
@AfterThrowing(value="execution(public int com.spring.aop.AtitheticCalculator.*(..))",throwing = "ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex){
String methodName=joinPoint.getSignature().getName();
System.out.println("The method "+methodName+" occurs exception:"+ex);
}
public class Main {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
AtitheticCalculator atitheticCalculator=ctx.getBean(AtitheticCalculator.class);
int result=atitheticCalculator.div(9,0);
System.out.println(result);
}
}
4.环绕通知
环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点.
甚至可以控制是否执行连接点.对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是 JoinPoint 的子接口, 允许控制何时执行, 是否执行连接点.
在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法. 如果忘记这样做就会导致通知被执行了, 但目标方法没有被执行.
注意: 环绕通知的方法需要返回目标方法执行之后的结果, 即调用 joinPoint.proceed(); 的返回值, 否则会出现空指针异常 。
ex:
/**
*
*环绕通知需要携带ProceedingJoinPoint类型的参数
*
*环绕通知类似于动态代理的全过程:ProceedingJoinPint 类型的参数可以决定是否执行目标方法
* 且环绕通知必须要有返回值,返回值必为目标方法的返回值
*/
@Around("execution(public int com.spring.aop.AtitheticCalculator.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result=null;
String methodName=pjd.getSignature().getName();
try {
//前置
System.out.println("The method "+methodName+" begin with "+Arrays.asList(pjd.getArgs()));
//执行目标方法
result=pjd.proceed();
//返回
System.out.println("The method "+methodName+" ends whith "+result);
} catch (Throwable throwable) {
//异常通知
System.out.println("The method occors exception:" +throwable);
throw new RuntimeException(throwable);//为了防止返回值result为null的情况
}
//后置
System.out.println("The method "+methodName+" ends");
return result;
}
public class Main {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
AtitheticCalculator atitheticCalculator=ctx.getBean(AtitheticCalculator.class);
int result=atitheticCalculator.div(9,0);
System.out.println(result);
}
}
最后讲一下切面的优先级:
在同一个连接点上应用不止一个切面时, 除非明确指定, 否则它们的优先级是不确定的.切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定.实现 Ordered 接口, getOrder() 方法的返回值越小, 优先级越高.若使用 @Order 注解, 序号出现在注解中
ex:
那么上一个切面会先得到执行,后面一个切面会在后面执行。