iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0

Review

AOP是在處理Cross-cutting concerns,將某段代碼(日誌)動態切入(不把日誌程式hardcode到業務邏輯方法中)到指定方法(加減乘除)的指定位置進行運行。
昨天我們學到了AOP中如何透過JDK動態代理與CGLIB代理來達到將程式碼動態切入,並透過pointcut expression決定要切入哪些方法

今日我們將探討AOP的Advice如何運作

Advice的執行順序

Advice的執行flow可以參考下面的順序,執行順性分為兩種,正常執行與異常執行只差在最後一個執行步驟(@AfterReturning/AfterThrowing)

try{
    @Before
    method.invoke(obj,args);
    @AfterReturning
}catch(){
    @AfterThrowing
}finally{
    @After
}

正常執行

@Before -> @After -> @AfterReturning

異常執行

@Before -> @Afte -> @AfterThrowing

<?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>
    <!-- 啟動AOP   -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
@Service
public class MyCalculator {
    public int add(int i, int j) {
        int result = i+j;
        return result;
    }

    public int sub(int i, int j) {
        int result = i-j;
        return result;
    }

    public int mul(int i, int j) {
        int result = i*j;
        return result;
    }

    public int div(int i, int j) {
        int result = i/j;
        return result;
    }
}
@Aspect
@Component
public class LogUtils {
   
    @Before("execution(public int com.swj.MyCalculator.*(int,int))")
    public static void logStart(){
        System.out.println("method start");
    }
   
    @AfterReturning("execution(public int com.swj.MyCalculator.*(int,int))")
    public static void logReturn(){
        System.out.println("method completed,result is");
    }

    @After("execution(public int com.swj.MyCalculator.*(int,int))")
    public static void logEnd(){
        System.out.println("method end");
    }
   
    @AfterThrowing("execution(public int com.swj.MyCalculator.*(int,int))")
    public static void logException(){
        System.out.println("method throws exception");
    }
}
@Test
public void testDay20(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("bean20.xml");
    System.out.println("容器啟動完成....");
    MyCalculator calculator = ioc.getBean(MyCalculator.class);
    calculator.add(1,1);
    System.out.println("===================");
    calculator.div(1,0);
}

Result
https://ithelp.ithome.com.tw/upload/images/20221005/201280843QzS6owUmR.jpg

取得通知方法的訊息

JointPoint

參數加入JointPoint,JointPoint封裝目標訪法的資訊
https://ithelp.ithome.com.tw/upload/images/20221005/201280847lPRbzoNYU.jpg

@Aspect
@Component
public class LogUtils {
    @Before("execution(public int com.swj.MyCalculator.*(int,int))")
    public static void logStart(JoinPoint jointPoint){
        //取得方法名稱,與parameter
        System.out.println("["+jointPoint.getSignature().getName()+"] method start,parameter:"+Arrays.asList(jointPoint.getArgs()));
    }

    @AfterReturning("execution(public int com.swj.MyCalculator.*(int,int))")
    public static void logReturn(JoinPoint joinPoint){
        System.out.println("["+joinPoint.getSignature().getName()+"] method completed,result is");
    }

    @After("execution(public int com.swj.MyCalculator.*(int,int))")
    public static void logEnd(JoinPoint joinPoint){
        System.out.println("["+joinPoint.getSignature().getName()+"] method end");
    }

    @AfterThrowing("execution(public int com.swj.MyCalculator.*(int,int))")
    public static void logException(JoinPoint joinPoint){
        System.out.println("["+joinPoint.getSignature().getName()+"] method throws exception");
    }

}
@Test
public void testDay20(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("bean20.xml");
    System.out.println("容器啟動完成....");
    MyCalculator calculator = ioc.getBean(MyCalculator.class);
    calculator.add(1,1);
}

Result
https://ithelp.ithome.com.tw/upload/images/20221005/20128084w3rsCAnSQw.jpg

@AfterReturning取得返回值訊息

Advice是由spring透過反射調用,所以需要告訴spring哪一個參數接收返回值訊息

  • 方法參數需加一個返回參數
  • 透過pointcut expression告訴Spring返回參數的名稱,讓Spring可以把返回值assign給它
@AfterReturning(value = "execution(public int com.swj.MyCalculator.*(int,int))",returning = "result")
public static void logReturn(JoinPoint joinPoint, Object result){
    System.out.println("["+joinPoint.getSignature().getName()+"] method completed,result is "+result);
}

取得exceptin訊息

Advice是由spring透過反射調用,所以需要告訴spring哪一個參數接收exception訊息

  • 方法參數需加一個承接Exception的參數
  • 透過pointcut expression告訴Spring返回參數的名稱,讓Spring可以把exception拋給它
@AfterThrowing(value = "execution(public int com.swj.MyCalculator.*(int,int))",throwing = "exception")
public static void logException(JoinPoint joinPoint,Exception exception){
    System.out.println("["+joinPoint.getSignature().getName()+"] method throws exception:"+exception.getMessage());
}
@Test
public void testDay20(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("bean20.xml");
    System.out.println("容器啟動完成....");
    MyCalculator calculator = ioc.getBean(MyCalculator.class);
    calculator.add(1,1);
    System.out.println("=================");
    calculator.div(1,0);
}

Result
https://ithelp.ithome.com.tw/upload/images/20221005/20128084z831FVB23f.jpg


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

尚未有邦友留言

立即登入留言