iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
1
Modern Web

美麗的邂逅-與安室....伊春系列 第 21

過路劫財 (AOC)

  • 分享至 

  • xImage
  •  
此路是我開,此樹是我栽,要從此路過,留下買路財。
  • AOP=Aspect Orientation Programming
  • @Aspect [class]這裡面有門道。
  • @PointCut [method]路障(攔路點)的集合, 每一個實際作用的點,稱作 Joint Point。
  • @Around, @Before, @After, @AfterReturning, @AfterThrowing [method] 攔路土匪。
  • 通知(Advice) 在連接點上執行的程式 (@Around, @Before, @After, @AfterReturning, @AfterThrowing)。
  • 目标对象(Target object) 在誰身上切,實際作用的對象。

所謂 AOC (Aspect Orientation Programming) 就是在程式特定的函式,安放一些路障,只要這些函式被呼叫前/後,執行指定的工作。
在這一回中,由前例 simpleDemo 繼續往下發展。
在pom.xml, <dependencies> 中增加

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

在 src/main/resources/application.properties 中增加幾行

# AOP settings
spring.aop.auto=true
spring.aop.proxy-target-class=true

假設 Target,目標是 RoutingController 中的攔截相關 Routing 的指令, 我們以 System.out.println()的方式列印相關的信息,看到底哪些函式被執行,並知道其執行順序。

@GetMapping({"/", "/hello"})
public String sayHello() {
    return "hello";
}
@GetMapping("/hello/{name}")
public String byRestfulGet(@PathVariable String name, Model model) {
    desc.setName(name);
    model.addAttribute("name", desc.getName());
    model.addAttribute("desc", desc.getDescripion());
    System.out.println("=== byRestfulGet ===");
    return "hello_name";
}

留意 sayHello() 沒有參數,而 byRestfulGet() 有二個參數,同時為了顯示執行的次序,我們在此加上列印 == byRestfulGet ==, 方便閱讀,AOP的程式如下:

@Aspect
@Component
public class AopDemo {
    @Pointcut("execution(public * com.example.demo.RoutingController.*(..))")
    public void myPointcut() {
    	System.out.println("--- AppDemo.myPointCut ---");
    }
    
    @Before("myPointcut()")
    public void myBefore(JoinPoint jp) {
    	System.out.println("... before ...");
    	System.out.println(". name: "+jp.getSignature().getName());
    	System.out.println(". type: "+jp.getSignature().getDeclaringTypeName());
    	if(jp.getArgs().length>0) System.out.println(". - " + jp.getArgs()[0]);
    }
    
    @After("myPointcut()") 
    public void myAfter() {
    	System.out.println("... after ...");
    }    
    @Around("myPointcut()")
    public void myAround() {
    	System.out.println("... around ...");
    }
}

其中,@Aspect,@Component,指明這個類別是用於 AOP, @Pointcut 指明,攔截點是類別 RoutingController 中所有的公開(public) 函式,注意,myPointcut()函式並不會真正被執行,因此一般都寫成空的指令,即:

@PointCut(...)
public void myPointcut() { }

@Before 是指在路障點前執行 (myBefore(JointPoint j)),JointPoint 可以傳入在執行時,切點的參數。執行的函數也可以不傳入任何參數。除了 @Before 之外,還有 @After (在路障後執行)。@Around 比較特殊,會在 (pjp.)proceed() 前後執行。注意,若沒有呼叫 proceed() 函式,那原來的被攔截的程式將不會被執行,proceed() 就是去執行被攔截的程式。同時 @Around 也會變更路由。如上例中,會回傳輸入的路徑 /hotel/Mattrew 因此會指向 /hotel/mattrew.jsp (而不是 hello_one.jsp)。若是要保留指向 hello_one.jsp,則程式必須改成如下編碼,回傳 proceed() 的結果。

@Around("myPointcut()")
public Object myAround(final ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("... around before proceed ...");
    Object ret_value = pjp.proceed();
    System.out.println("... around after proceed ...");
    return ret_value;
}

執行的結果如下(http://localhost:8080/hello/Luke):
https://ithelp.ithome.com.tw/upload/images/20191006/20120951IvFFXk98iM.png
由Console欄的信息可以看出,其執行的次序是: @Around > @Before > [proceed()] > @Around > @After,至於各道 "令符" @Pointcut(…), @within(…), @Target(…), @annotation(…),@args(…), args(…) 其中參數的格式都有許多變化,各位讀者可以參考其他文章,或是我們有緣再相會。


上一篇
倒轉乾坤 (IOC)
下一篇
競合一: 後端 (Link/Spring Boot)
系列文
美麗的邂逅-與安室....伊春30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言