[Spring Boot實戰系列] - No.5 Spring boot AOP 示例

Spring boot AOP 示例

在之前的文章中,介紹過Spring 的AOP與AspectJ相關的內容。最近實驗室的一個項目又用到了springboot的AOP,在網上調研了一下發現了幾個配置極其簡單但功能很完善的示例,在這裏總結一下。AOP相關的原理及含義不再解釋,參考之前的文章。

1. 前期代碼準備

創建一個Springboot項目,在項目中編寫一個IndexController,一個User實體類,以及一個service(爲了簡單起見我直接編寫了Service的實現,而沒有按照接口-實現的方式)

IndexController

@RestController
public class IndexController {

    @Autowired
    MyService myService;

    @GetMapping(value = "hello")
    public String hello(){
        myService.sayHello("Greet to everyone");
        return "hhh";
    }
}

hello()函數中,我們調用myservicesayHello函數,並向前臺發送字符串hhh

User

public class User {
    private int id;
    private String name;
    public User(){

    }
    public User(int id,String name){
        this.id = id;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
}

MyService

@Service
public class MyService {
    public void sayHello(String greet){
        System.out.println("Hello, I'm fucntion sayHello of MyService");
    }
    public void introduce(User user){
        System.out.println("Hello My name is "+ user.getName());
    }
}
2. 定義切面

在項目中定義一個切面,如下所示:

RuleAspect

@Aspect
@Component
public class RuleAspect {

    @Pointcut("execution(* com.example.demo.service.MyService.sayHello(..))")
    public void pointCutName(){}

    @Before(value = "execution(* com.example.demo.service.MyService.sayHello(..))")
    public void beforeFunc(){
        System.out.println("Function before sayHello");
    }

    @After("pointCutName()")
    public void afterFunc(){
        System.out.println("Function after sayHello");
    }
    
    @Around("execution(* com.example.demo.controller.IndexController.hello(..))")
    public Object aroundHelloCtrl(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Before");
        Object res = proceedingJoinPoint.proceed();
        System.out.println("After");
        return res;
    }
}

要解釋的地方有以下幾點:

  • 定義切點有幾種方法,可以使用@Pointcut首先定義一個函數,然後在後面的註解中直接使用,例如代碼中的@After標註的函數,也可像@Before的示例一樣,直接在註解後面寫明完整的函數路徑

  • @Before,@After,@Around分別對應在前置通知(在連接點執行之前)、後置通知,和環繞通知。三種通知的執行順序如下(不考慮@AfterReturning@AfterReturning)

在這裏插入圖片描述

  • 在環繞通知@Around中,我們看到函數簽名處有參數ProceedingJoinPoint,這個參數是獲取切入點函數的參數。我們可以看到,在示例函數中,我們首先打印Before,然後調用proceedingJoinPoint.proceed()來執行切入點函數,然後在函數結束後打印After

    同時,要注意的是,如果你的切入點函數有返回值,那麼@Around註解的通知函數一定也要有返回值,否則切入點函數不能正常返回結果

啓動程序,我們在postman中輸入http://localhost:8080/hello

控制檯結果如下:

Before
Function before sayHello
Hello, I'm fucntion sayHello of MyService
Function after sayHello
After

調用順序參見上面的順序圖

3. 獲取切入點函數中的參數

獲取切入點函數中的參數,在網上有很多采用反射的方法來獲取的。雖然也能很好的完成功能,但過程有點冗雜。還是推薦使用aspectj中基於註解傳遞參數的方法

我們向Controller中添加一個控制函數,專門用來展示連接點參數傳遞的效果。該函數從url中拿到參數作爲函數形參

@GetMapping(value = "getArgs")
public String getArgs(@RequestParam("key")String key,@RequestParam("value") String value){
    return "hhh";
}

在切面中定義一個通知,獲取連接點中的參數值

@Around(value = "execution(* com.example.demo.controller.IndexController.getArgs(..)) && args(key,value)")
public Object aroundSayHello(ProceedingJoinPoint joinPoint,String key,String value) throws Throwable {
    System.out.printf("The args of this method is %s and %s \n",key,value);
    return joinPoint.proceed();
}

方法很簡單,在execution後邊添加 && args(key,value),並在函數的簽名處聲明對應的參數。要注意的是args()後面的參數,必須和切入點函數對應的簽名是一樣的,即形參的類型和個數、順序必須一樣,否則無法調用通知函數

postman中輸入以下地址:http://localhost:8080/getArgs?key=123&value=456

The args of this method is 123 and 456 

該方法不僅可以傳入基本類型,還可以傳入我們定義的實體,示例如下

Controller中添加控制函數:

@GetMapping(value = "intro")
public void intro(){
	myService.introduce(new User(123456,"ming"));
}

在切面中新添加一個環繞通知,用來測試接受切入點實體參數

@Before("execution(* com.example.demo.service.MyService.introduce(..)) && args(user)")
public void beforeIntro(User user){
    System.out.println(user.getName());
}

postman中輸入http://localhost:8080/intro

ming
Hello My name is ming
4. 使用註解聲明切入點

還有一種方法是自定義一個註解,然後在切入點函數上添加這個註解,即基於註解的AOP形式。在這裏就不贅述,個人還是比較喜歡這種在通知上聲明函數路徑的方式,有興趣的同學可以在網上調研學習一下。另外所有代碼基本都在文章裏,就不在上傳完整項目了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章