iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0
Software Development

這些年,我們早該學會的Spring Framework系列 第 22

Day22 - AOP (4) 多Aspect的Advice運行順序 與 XML設定AOP

  • 分享至 

  • xImage
  •  

Review

AOP是在處理Cross-cutting concerns,將某段代碼(日誌)動態切入(不把日誌程式hardcode到業務邏輯方法中)到指定方法(加減乘除)的指定位置進行運行。
昨天我們學到了AOP中Around Advice的執行順序與透過空方法可以將pointcut expression抽出來重用

今日我們將探討AOP在多個Aspect的Advice的運行順序

多Aspect的Advice運行順序

workflow

  • 切面類可以透過@Order決定哪個切面先執行,否則將照字母順序
  • Advice的執行順序會是後進先出
    https://ithelp.ithome.com.tw/upload/images/20221007/20128084K2hBhQFpkS.png

測試案例

Log切面

@Aspect
@Component
@Order(1)
public class LogUtils {
    @Pointcut(value = "execution(public int com.swj.MyCalculator.*(..))")
    public void pointCutExpressionShare(){}

    @Around("pointCutExpressionShare()")
    public Object aroundAdvice(ProceedingJoinPoint pjp){
        Object[] params = pjp.getArgs();
        String methodName = pjp.getSignature().getName();
        Object proceed =null;
        try {
            //Before
            System.out.println("[LogUtils-Around Before]"+"["+methodName+"]");
            //目標方法invoke
            proceed = pjp.proceed(params);
            //AfterReturning
            System.out.println("[LogUtils-Around AfterReturning]"+"["+methodName+"]");
        }catch (Throwable e){
            //AfterThrowing
            System.out.println("[LogUtils-Around AfterThrowing]"+"["+methodName+"]");
//            throw new RuntimeException(e);
        }finally {
            //After
            System.out.println("[LogUtils-Around After]"+"["+methodName+"]");
        }
        return proceed;
    }
    @Before("pointCutExpressionShare()")
    public static void logStart(JoinPoint jointPoint){
        System.out.println("[LogUtils-Before]"+"["+jointPoint.getSignature().getName()+"] method start,parameter:"+Arrays.asList(jointPoint.getArgs()));
    }

    @AfterReturning(value = "pointCutExpressionShare()",returning = "result")
    public static void logReturn(JoinPoint joinPoint, Object result){
        System.out.println("[LogUtils-AfterReturning]"+"["+joinPoint.getSignature().getName()+"] method completed,result is "+result);
    }

    @After("pointCutExpressionShare()")
    public static void logEnd(JoinPoint joinPoint){
        System.out.println("[LogUtils-After]"+"["+joinPoint.getSignature().getName()+"] method end");
    }

    @AfterThrowing(value = "pointCutExpressionShare()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
        System.out.println("[LogUtils-AfterThrowing]"+"["+joinPoint.getSignature().getName()+"] method throws exception:"+exception);
    }

}

Audit切面

@Aspect
@Component
@Order(2)
public class AuditUtils {

//    @Around("com.swj.LogUtils.pointCutExpressionShare()")
    public Object aroundAdvice(ProceedingJoinPoint pjp){
        Object[] params = pjp.getArgs();
        String methodName = pjp.getSignature().getName();
        Object proceed =null;
        try {
            //Before
            System.out.println("[AuditUtils-Around Before]"+"["+methodName+"]");
            //目標方法invoke
            proceed = pjp.proceed(params);
            //AfterReturning
            System.out.println("[AuditUtils-Around AfterReturning]"+"["+methodName+"]");
        }catch (Throwable e){
            //AfterThrowing
            System.out.println("[AuditUtils-Around AfterThrowing]"+"["+methodName+"]");
        }finally {
            //After
            System.out.println("[AuditUtils-Around After]"+"["+methodName+"]");
        }
        return proceed;
    }
    @Before("com.swj.LogUtils.pointCutExpressionShare()")
    public static void logStart(JoinPoint jointPoint){
        //取得方法名稱,與parameter
        System.out.println("[AuditUtils-Before]"+"["+jointPoint.getSignature().getName()+"] method start,parameter:"+Arrays.asList(jointPoint.getArgs()));
    }

    @AfterReturning(value = "com.swj.LogUtils.pointCutExpressionShare()",returning = "result")
    public static void logReturn(JoinPoint joinPoint, Object result){
        System.out.println("[AuditUtils-AfterReturning]"+"["+joinPoint.getSignature().getName()+"] method completed,result is "+result);
    }

    @After("com.swj.LogUtils.pointCutExpressionShare()")
    public static void logEnd(JoinPoint joinPoint){
        System.out.println("[AuditUtils-After]"+"["+joinPoint.getSignature().getName()+"] method end");
    }

    @AfterThrowing(value = "com.swj.LogUtils.pointCutExpressionShare()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
        System.out.println("[AuditUtils-AfterThrowing]"+"["+joinPoint.getSignature().getName()+"] method throws exception:"+exception);
    }
}

Result
https://ithelp.ithome.com.tw/upload/images/20221007/201280843Ayar1jrpI.jpg

XML設定AOP

  1. 將目標類和切面類加到ioc中,@Componet
  2. 告訴spring哪個是切面類
  3. 設定切面類中的Advice方法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!--    <context:component-scan base-package="com.swj"></context:component-scan>-->
<!--    &lt;!&ndash; 啟動AOP   &ndash;&gt;-->
<!--    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>-->
    <bean id="myCalculator" class="com.swj.MyCalculator"></bean>
    <bean id="logUtils" class="com.swj.LogUtils"></bean>
    <bean id="auditUtils" class="com.swj.AuditUtils"></bean>
    
    <aop:config>
        <!-- 將pointcut expression抽出來       -->
        <aop:pointcut id="myPointcut" expression="execution(public int com.swj.MyCalculator.*(..))"/>
        <aop:aspect ref="logUtils">
            <aop:before method="logStart" pointcut-ref="myPointcut"></aop:before>
            <aop:after-returning method="logReturn" pointcut-ref="myPointcut" returning="result"></aop:after-returning>
            <aop:after-throwing method="logException" pointcut-ref="myPointcut" throwing="exception"></aop:after-throwing>
            <aop:after method="logEnd" pointcut-ref="myPointcut"></aop:after>
            <aop:around method="aroundAdvice" pointcut-ref="myPointcut"></aop:around>
        </aop:aspect>
        <aop:aspect ref="auditUtils">
            <aop:before method="logStart" pointcut-ref="myPointcut"></aop:before>
            <aop:after-returning method="logReturn" pointcut-ref="myPointcut" returning="result"></aop:after-returning>
            <aop:after-throwing method="logException" pointcut-ref="myPointcut" throwing="exception"></aop:after-throwing>
            <aop:after method="logEnd" pointcut-ref="myPointcut"></aop:after>
            <aop:around method="aroundAdvice" pointcut-ref="myPointcut"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>
@Test
public void testDay22(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("bean22.xml");
    System.out.println("容器啟動完成....");
    MyCalculator calculator = ioc.getBean(MyCalculator.class);
    calculator.add(1,1);
    System.out.println("=================");
    calculator.div(1,0);
}

Result

  • Before、Around_Before順序因xml設定檔順序決定
  • 內部切面類執行完畢,會先執行外部切面的Around Advice
  • 當AfterThrowing發生時,會因Around_AfterThrowing沒有拋出異常造成後面執行@fterReturning而非AfterThrowing且回傳值為null
    https://ithelp.ithome.com.tw/upload/images/20221010/20128084M4swYPQH9O.jpg

上一篇
Day21 - AOP (3) Around advice
下一篇
Day23 - JdbcTemplate (1)
系列文
這些年,我們早該學會的Spring Framework30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言