Spring中配置AOP的两种方式以及后置通知顺序的问题

准备工作:目标类AdviceService, 注解方式的通知类MyAdvice, XML方式配置的通知类MyAdviceXML

XML配置所用的通知类 MyAdviceXML:

public class MyAdviceXML {
    //前置通知
    public void before() {
        System.err.println("Before -- 前置通知 (XML配置)");
    }

    //后置通知
    public void afterReturning() {
        System.err.println("AfterReturning -- 后置通知 (XML配置)");
    }

    //异常通知
    public void afterThrowing() {
        System.err.println("AfterThrowing -- 异常通知 (XML配置)");
    }

    //最终通知
    public void after() {
        System.err.println("After -- 最终通知 (XML配置)");
    }

    //环绕通知
    public Object around(ProceedingJoinPoint pjp) {
        try {
            System.err.println("Before -- 前置通知 (环绕通知 - XML配置)");
            Object returnValue = pjp.proceed(pjp.getArgs());
            System.err.println("AfterReturning -- 后置通知 (环绕通知 - XML配置)");
            return returnValue;
        } catch (Throwable e) {
            System.err.println("AfterThrowing -- 异常通知 (环绕通知 - XML配置)抛出的异常:" + e);
            throw (RuntimeException) e;
        } finally {
            System.err.println("After -- 最终通知 (环绕通知 - XML配置)");
        }
    }
}

XML对应的AOP配置:

    <!--注册通知类-->
    <bean id="myAdviceXML" class="com.zzq.util.MyAdviceXML"/>

    <!--配置AOP-->
    <aop:config>
        <!--配置切入点表达式-->
        <aop:pointcut id="pointcut" expression="execution(* com.zzq.service.impl.AdviceService.*(..))"/>
        <!--配置切面-->
        <aop:aspect id="myAdvice" ref="myAdviceXML">
            <!--前置通知-->
            <aop:before method="before" pointcut-ref="pointcut"/>
            <!--后置通知-->
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
            <!--异常通知-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"/>
            <!--最终通知-->
            <aop:after method="after" pointcut-ref="pointcut"/>
            <!--环绕通知-->
            <aop:around method="around" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

注解配置的通知类 MyAdvice :

@Component
@Aspect
public class MyAdvice {
    //通用切入点表达式
    @Pointcut("execution(* com.zzq.service.impl.AdviceService.*(..))")
    private void pointcut() {
    }

    //前置通知,同时获取切入点方法的形参
    @Before(value = "pointcut() && args(a,b)", argNames = "a,b")  //引用的切入点表达式后也可以直接追加 && args()
    public void before(int a, int b) {
        System.err.println("Before -- 前置通知 (注解配置)" + "方法参数:" + a + ", " + b);
    }

    //后置通知,同时获取方法返回值
    @AfterReturning(value = "pointcut()", returning = "qwer")
    public void afterReturning(Object qwer) {
        System.err.println("AfterReturning -- 后置通知 (注解配置)" + "方法返回值:" + qwer);
    }

    //异常通知,同时获取方法抛出的异常
    @AfterThrowing(value = "pointcut()", throwing = "asdf")
    public void afterThrowing(Throwable asdf) {
        System.err.println("AfterThrowing -- 异常通知 (注解配置)" + "抛出的异常:" + asdf);
    }

    //后置通知
    @After("pointcut()")
    public void after() {
        System.err.println("After -- 最终通知 (注解配置)");
    }

    //环绕通知
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            System.err.println("Before -- 前置通知 (环绕通知 - 注解配置)");
            Object returnValue = pjp.proceed(pjp.getArgs());
            System.err.println("AfterReturning -- 后置通知 (环绕通知 - 注解配置)");
            return returnValue;
        } catch (Throwable e) {
            System.err.println("AfterThrowing -- 异常通知 (环绕通知 - 注解配置)抛出的异常:" + e);
            throw (RuntimeException) e;
        } finally {
            System.err.println("After -- 最终通知 (环绕通知 - 注解配置)");
        }
    }
}

目标类:

@Service
public class AdviceService {
    public String method(int a, int b) {
        //int i=1/0;  //制造异常
        System.err.println("-----------------");
        System.err.println("执行切入点方法...");
        System.err.println("-----------------");
        return (a + b) + "";
    }
}

测试环境的配置类:

@Configuration
@ComponentScan("com.zzq")
@ImportResource("classpath:application.xml") //导入xml配置
@EnableAspectJAutoProxy(exposeProxy = true)  //开启Spring注解AOP的支持
public class SpringConfiguration {
}

测试通知

@Test
public void testAdvice() {
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    context.getBean(AdviceService.class).method(1, 2);
}

无异常的结果:

Before -- 前置通知 (XML配置)
Before -- 前置通知 (环绕通知 - XML配置)
Before -- 前置通知 (环绕通知 - 注解配置)
Before -- 前置通知 (注解配置)方法参数:1, 2
-----------------
执行切入点方法...
-----------------
AfterReturning -- 后置通知 (注解配置)方法返回值:3
After -- 最终通知 (注解配置)
AfterReturning -- 后置通知 (环绕通知 - 注解配置)
After -- 最终通知 (环绕通知 - 注解配置)
AfterReturning -- 后置通知 (环绕通知 - XML配置)
After -- 最终通知 (环绕通知 - XML配置)
After -- 最终通知 (XML配置)
AfterReturning -- 后置通知 (XML配置)

Process finished with exit code 0

有异常的结果:

Before -- 前置通知 (XML配置)
Before -- 前置通知 (环绕通知 - XML配置)
Before -- 前置通知 (环绕通知 - 注解配置)
Before -- 前置通知 (注解配置)方法参数:1, 2
AfterThrowing -- 异常通知 (注解配置)抛出的异常:java.lang.ArithmeticException: / by zero
After -- 最终通知 (注解配置)
AfterThrowing -- 异常通知 (环绕通知 - 注解配置)抛出的异常:java.lang.ArithmeticException: / by zero
After -- 最终通知 (环绕通知 - 注解配置)
AfterThrowing -- 异常通知 (环绕通知 - XML配置)抛出的异常:java.lang.ArithmeticException: / by zero
After -- 最终通知 (环绕通知 - XML配置)
After -- 最终通知 (XML配置)
AfterThrowing -- 异常通知 (XML配置)

java.lang.ArithmeticException: / by zero

Process finished with exit code 0

总结:
以上列出了我们熟知的Spring通知的两种配置方式。其中需要我们注意的是:当我们使用注解配置通知时,最终通知的执行顺序与XML配置不同;XML配置的最终通知是在方法结束最终执行,而注解不同;
注解配置通知的执行顺序如下:

try{
    try{
        //@Before  -- 首先执行前置通知
        method.invoke(..); -- 然后执行切入点方法
    }finally{
        //@After  -- 再而肯定会执行最终通知 --- 注解配置的注意点
    }
    //@AfterReturning  -- 如果没有异常,则继续执行后置通知
    return;  -- 返回结果
}catch(){
    //@AfterThrowing  -- 如果有异常,则执行异常通知
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值