1.通知类型
前面我们介绍了这是环绕通知@Around--此注解标注的通知方法在目标方法前后都被执行,还有
前置通知@Before--此注解标注的通知方法在目标方法前都被执行,
后置通知@After--此注解标注的通知方法在目标方法前都被执行,无论是否有异常都会执行,
返回后通知@AfterReturning--此注解标注的通知方法在目标方法前都被执行,有异常不会执行,
异常后通知@AfterThrowing--此注解标注的通知方法在发生异常后执行,
通知类型就是定义重复代码的位置是在哪里,重点区分后三个。
@Slf4j
@Component
@Aspect
public class MyAspect1 {
//前置通知
@Before("execution(* com.itheima.service.*.*(..))")
public void before(JoinPoint joinPoint){
log.info("before ...");
}
//环绕通知
@Around("execution(* com.itheima.service.*.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.info("around before ...");
//调用目标对象的原始方法执行
Object result = proceedingJoinPoint.proceed();
//原始方法如果执行时有异常,环绕通知中的后置代码不会在执行了
log.info("around after ...");
return result;
}
//后置通知
@After("execution(* com.itheima.service.*.*(..))")
public void after(JoinPoint joinPoint){
log.info("after ...");
}
//返回后通知(程序在正常执行的情况下,会执行的后置通知)
@AfterReturning("execution(* com.itheima.service.*.*(..))")
public void afterReturning(JoinPoint joinPoint){
log.info("afterReturning ...");
}
//异常通知(程序在出现异常的情况下,执行的后置通知)
@AfterThrowing("execution(* com.itheima.service.*.*(..))")
public void afterThrowing(JoinPoint joinPoint){
log.info("afterThrowing ...");
}
}
注:
1.Around是环绕在方法帮的,所以里面目标方法前面的代码比前置通知后执行,目标方法后面的代码比后置通知早执行。
2.环绕通知中原始方法调用是有异常,通知中环绕后的代码逻辑额不会执行
3.环绕通知方法的返回值,必须指定为Object,来接受原始方法的返回值,否则原始方法执行完毕,是获取不到返回值的。
2.重复切入点表达式的抽取
很多情况下切入点表达式是重复的。
Spring提供了@PointCut注解,该注解的作用是将公共的切入点表达式抽取出来,需要的时候引用改切入点表达式即可。
例子:
原来:
//前置通知
@Before("execution(* com.itheima.service.*.*(..))")
//环绕通知
@Around("execution(* com.itheima.service.*.*(..))")
//后置通知
@After("execution(* com.itheima.service.*.*(..))")
//返回后通知(程序在正常执行的情况下,会执行的后置通知)
@AfterReturning("execution(* com.itheima.service.*.*(..))")
//异常通知(程序在出现异常的情况下,执行的后置通知)
@AfterThrowing("execution(* com.itheima.service.*.*(..))")
使用@PointCut注解提取后:
@Slf4j
@Component
@Aspect
public class MyAspect1 {
//切入点方法(公共的切入点表达式)
@Pointcut("execution(* com.itheima.service.*.*(..))")
private void pt(){
}
//前置通知(引用切入点)
@Before("pt()")
public void before(JoinPoint joinPoint){
}
//环绕通知
@Around("pt()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
}
//后置通知
@After("pt()")
public void after(JoinPoint joinPoint){
}
//返回后通知
@AfterReturning("pt()")
public void afterReturning(JoinPoint joinPoint){
}
//异常通知
@AfterThrowing("pt()")
public void afterThrowing(JoinPoint joinPoint){
}
}
注:当外部其他切面类中也要引用当前类中的切入点表达式,就需要吧private改为public,而在引用的时候,具体的语法为:全类名.方法名,具体形式如下:
@Slf4j
@Component
@Aspect
public class MyAspect2 {
//引用MyAspect1切面类中的切入点表达式
@Before("com.itheima.aspect.MyAspect1.pt()")
public void before(){
log.info("MyAspect2 -> before ...");
}
}
3.通知顺序
当在项目开发当中,我们定义了多个切面类,而多个切面类中多个切入点都匹配到了同一个目标方法,目标方法在运行的时候,这多个切面类地址的这些通知方法都会执行。
不同切面类中,默认按照切面类的类名字母排序:
目标方法前的通知方法:字母排名靠前的先执行
目标方法后的通知方法:字母排名靠前的后执行
如果我们想控制通知的执行顺序有两种方式:
1.修改切面类的类名(一般不会这样做)
2.使用Spring提供的order注解。
例:
@Slf4j
@Component
@Aspect
@Order(2) //切面类的执行顺序(前置通知:数字越小先执行; 后置通知:数字越小越后执行)
public class MyAspect2 {
//前置通知
@Before("execution(* com.itheima.service.*.*(..))")
public void before(){
log.info("MyAspect2 -> before ...");
}
//后置通知
@After("execution(* com.itheima.service.*.*(..))")
public void after(){
log.info("MyAspect2 -> after ...");
}
}
@Slf4j
@Component
@Aspect
@Order(3) //切面类的执行顺序(前置通知:数字越小先执行; 后置通知:数字越小越后执行)
public class MyAspect3 {
//前置通知
@Before("execution(* com.itheima.service.*.*(..))")
public void before(){
log.info("MyAspect3 -> before ...");
}
//后置通知
@After("execution(* com.itheima.service.*.*(..))")
public void after(){
log.info("MyAspect3 -> after ...");
}
}
@Slf4j
@Component
@Aspect
@Order(1) //切面类的执行顺序(前置通知:数字越小先执行; 后置通知:数字越小越后执行)
public class MyAspect4 {
//前置通知
@Before("execution(* com.itheima.service.*.*(..))")
public void before(){
log.info("MyAspect4 -> before ...");
}
//后置通知
@After("execution(* com.itheima.service.*.*(..))")
public void after(){
log.info("MyAspect4 -> after ...");
}
}