Spring框架之Day08多切面AOP执行顺序和AOP使用场景
环绕通知@Around执行顺序
环绕前置通知-》环绕正常执行通知-》环绕后置通知
出现异常后:环绕前置通知-》环绕异常通知-》环绕后置通知
如果存在四大通知混合情况,执行顺序为:
普通前置-》环绕前置-》环绕正常执行通知-》环绕后置通知-》普通后置-》普通正常执行通知
出现异常后:普通前置-》环绕前置-》环绕异常通知-》环绕后置-》普通后置-》!普通正常执行通知(实则异常它仍执行结果为null)
多切面执行顺序
无环绕
根据先进后出的原则(栈)谁最后接触核心目标方法,谁先执行完后置通知方法。
举例:
存在LogUtilsAOP和MultiAOP两个工具切面类,M后进入,先出来。即执行顺序为L前置通知-》M前置通知-》M最终结束-》M正常执行完-》L最终结束-》L正常执行完
【[LogUtilsAOP]div】方法开始执行,用的参数列表为【[4, 2]】
【[MultiAOP]div】方法开始执行,用的参数列表为【[4, 2]】
[MultiAOP]div()方法finally最终结束了
【[MultiAOP]div】方法正常执行完成,计算结果为:2
[LogUtilsAOP]div()方法finally最终结束了
【[LogUtilsAOP]div】方法正常执行完成,计算结果为:2
有环绕
环绕单独在外
三个类(单独环绕工具切面类):LogUtilsAOP(1)、MultiAOP(2)、LogUtilsAroundAOP(3)
每个类分别使用注解指定顺序@Order(1) @Order(2) @Order(3)
数字越小越先进入,最后才执行完出去。即执行顺序为:L前置-》M前置-》环绕前置-》环绕正常执行完-》环绕后置-》M后置最终执行-》M->正常执行完-》L后置最终执行-》L正常执行完
【[LogUtilsAOP]div】方法开始执行,用的参数列表为【[4, 2]】
【[MultiAOP]div】方法开始执行,用的参数列表为【[4, 2]】
【环绕前置通知】div()方法开始执行
【环绕正常执行通知】div()方法返回,计算结果返回值为2
【环绕后置通知】div()方法最终结束
[MultiAOP]div()方法finally最终结束了
【[MultiAOP]div】方法正常执行完成,计算结果为:2
[LogUtilsAOP]div()方法finally最终结束了
【[LogUtilsAOP]div】方法正常执行完成,计算结果为:2
@Aspect
@Component
@Order(1)
public class LogUtilsAOP {
@Pointcut("execution(public int com.lwt.Calculator.impl.MyMathCal.*(int,int))")
public void myexecution(){
}
/**
* 想在执行目标方法之前运行@Before,在那个方法之前运行呢?写切入点表达式("myexecutioncution(public int 方法全描述)")
*/
// @Before("myexecution(public int com.lwt.Calculator.impl.MyMathCal.*(int,int))")
@Before("myexecution()")
public void logStart(JoinPoint joinPoint) { // 参数列表不可乱写
// System.out.println("[xxx]方法开始,它使用的参数是"+ Arrays.asList(objects));
// System.out.println("【"+method.getName()+"】方法开始执行,用的参数列表为【"+ Arrays.asList(objects) +"】");
System.out.println("【[LogUtilsAOP]"+joinPoint.getSignature().getName()+"】方法开始执行,用的参数列表为【"+Arrays.asList(joinPoint.getArgs())+"】");
}
/**
* 想在目标方法正常执行完成之后执行@AfterReturning
*/
@AfterReturning(value = "myexecution()",returning = "result")
public void logReturn(JoinPoint joinPoint,Object result) {
// System.out.println("【"+method.getName()+"】方法正常执行完成,计算结果为:"+Arrays.asList(objects));
System.out.println("【[LogUtilsAOP]"+joinPoint.getSignature().getName()+"】方法正常执行完成,计算结果为:"+result);
}
/**
* 想在目标方法出现异常之后执行@AfterThrowing
*/
@AfterThrowing(value = "myexecution()",throwing = "e")
public void logException(JoinPoint joinPoint,Exception e) {
// System.out.println(method.getName()+"()方法执行出现异常,异常信息是"+e.getCause());
System.out.println("[LogUtilsAOP]"+joinPoint.getSignature().getName()+"()方法执行出现异常,异常信息是"+e);
}
/**
* 想在方法结束后执行@After方法结束后
*/
@After("execution(public int com.lwt.Calculator.impl.MyMathCal.*(int,int))")
public void logFinally(JoinPoint joinPoint){
System.out.println("[LogUtilsAOP]"+joinPoint.getSignature().getName()+"()方法finally最终结束了");
}
}
/**
* L M N O P Q R S T U V W X Y Z
* @Author Li Weitong
* @Date 2020/12/10 10:34
*/
@Aspect
@Component
@Order(2) // 数值越小,优先级越高,切面的执行顺序
public class MultiAOP {
// 跨类引入全名切入点表达式抽取出的方法
@Before("com.lwt.Calculator.utils.LogUtilsAOP.myexecution()")
public static void logStart(JoinPoint joinPoint){
System.out.println("【[MultiAOP]"+joinPoint.getSignature().getName()+"】方法开始执行,用的参数列表为【"+ Arrays.asList(joinPoint.getArgs()) +"】");
}
@AfterReturning(value = "com.lwt.Calculator.utils.LogUtilsAOP.myexecution()",returning = "result")
public static void logReturn(JoinPoint joinPoint,Object result){
System.out.println("【[MultiAOP]"+joinPoint.getSignature().getName()+"】方法正常执行完成,计算结果为:"+result);
}
@AfterThrowing(value = "com.lwt.Calculator.utils.LogUtilsAOP.myexecution()",throwing = "e")
public static void logException(JoinPoint joinPoint,Exception e){
System.out.println("[MultiAOP]"+joinPoint.getSignature().getName()+"()方法执行出现异常,异常信息是"+e);
}
@After(value = "com.lwt.Calculator.utils.LogUtilsAOP.myexecution()")
public static void logFinally(JoinPoint joinPoint){
System.out.println("[MultiAOP]"+joinPoint.getSignature().getName()+"()方法finally最终结束了");
}
}
/**
* L M N O P Q R S T U V W X Y Z
* @Author Li Weitong
* @Date 2020/12/9 17:39
*/
@Aspect
@Component
@Order(3)
public class LogUtilsAroundAOP {
@Pointcut("execution(public int com.lwt.Calculator.impl.MyMathCal.*(int,int))")
public void myexecution(){
}
/*环绕通知4合1
环绕通知中有一个参数ProceedingJoinPoint pjp
*/
@Around("myexecution()")
public Object myAround(ProceedingJoinPoint pjp){
Object[] args = pjp.getArgs();
String name = pjp.getSignature().getName();
Object proceed = null;
try {
System.out.println("【环绕前置通知】"+name+"()方法开始执行");// @Before环绕前置通知
proceed = pjp.proceed(args); // 利用反射调用目标方法即可,等同于method.invoke(obj,args)
System.out.println("【环绕正常执行通知】"+name+"()方法返回,计算结果返回值为"+proceed);// @AfterReturning环绕正常执行返回通知
} catch (Throwable throwable) {
System.out.println("【环绕异常通知"+name+"()方法出现异常,异常信息:】"+throwable);// @AfterThrowing环绕异常通知
throwable.printStackTrace();
} finally {
System.out.println("【环绕后置通知】"+name+"()方法最终结束");// @After环绕后置通知
}
return proceed;// 反射调用的返回值返回出去
}
}
测试:
/*
无接口
*/
@Test
public void test2(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-configAOP1.xml");
// 从ioc容器中拿到目标对象,注意:如果想要用类型,一定用他的接口类型,不要用本类。但是,当不实现接口后,则用假神被代理类或通过默认首字母小写id来获取bean对象
MyMathCal bean1 = ioc.getBean(MyMathCal.class);
bean1.div(4,2);
System.out.println(bean1); // [email protected]
// ioc容器中保存的组件是代理对象class com.lwt.Calculator.impl.MyMathCal$$EnhancerBySpringCGLIB$$9b6eec87
System.out.println("ioc容器中保存的组件是代理对象"+bean1.getClass());
Object bean2 = ioc.getBean("myMathCal");
System.out.println(bean2);
System.out.println("ioc容器中保存的组件是代理对象"+bean2.getClass());
}
环绕在四大通知内
环绕优先于当前类的四大通知先进入执行,最后一个执行结束退出。