本篇將設定Spring AOP來對Controller的方法做log。
在使用SpringBoot打造記帳簿專案(十二)使用Log4j2 + SLF4J記錄日誌中設定了log4j,並在HelloController.hello()
中撰寫了log紀錄方法的開始和結束,但log程式混雜在業務邏輯中會讓程式看起來很雜亂,所以這邊要將log的執行改以AOP來處理。
首先在pom.xml
加入Spring Boot AOP的Maven dependency。
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>idv.matt</groupId>
<artifactId>moneynote</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>moneynote</name>
<description>moneynote</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
</project>
在src/main/java
下新增Aspect類別idv.matt.aop.LogAspect
如下。
LogAspect.java
package idv.matt.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* idv.matt.controller..*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass().getName());
logger.info("start");
}
@After("pointcut()")
public void after(JoinPoint joinPoint) {
Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass().getName());
logger.info("end");
}
}
Aspect類別記得加上@Component
,如此才能被Spring Boot掃描為bean。
AOP相關的觀念請參考AOP Concepts,簡單解釋幾點:
- Aspect:AOP的Aspect類別,用來設定切入點(Pointcut)及切入的邏輯(Advice),Aspect類別上須加上
@Aspect
,也就是上例的LogAspect
。 - Pointcut:要被AOP切入的位置,使用pointcut expression來表示,而Pointcut位置的Join point即為Advice施行的目標。上面的
@Pointcut("execution(* idv.matt.controller..*(..))")
即表示切入位置為idv.matt.controller
下的任意method。 - Joint point:Pointcut位置中實際的切入點,通常是一個method,也就是上面的
HelloController.hello()
- Advice:對joint point要施行的動作,有多種形式,如上面的掛有
@Before
及@After
的method即為Advice。
因為LogAspect
位在idv.matt.aop
,因此記得在SpringBootApplication(即MoneynoteApplication
)的@ComponentScan
設定掃描idv.matt.aop
,LogAspect
才會被註冊為bean。
package idv.matt.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = { "idv.matt.controller, idv.matt.aop" }) // <-- here
public class MoneynoteApplication {
public static void main(String[] args) {
SpringApplication.run(MoneynoteApplication.class, args);
}
}
將HelloController.hello()
中的log移除,改成如下:
HelloController.java
package idv.matt.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
String s = "hello moneynote";
System.out.println(s);
return s;
}
}
設定好後啟動應用程式,在瀏覽器輸入http://localhost:8080/moneynote/hello
,執行後會在console印出下面結果。
[2019-02-02 23:16:50,510][INFO][idv.matt.controller.HelloController:23] - start
hello moneynote
[2019-02-02 23:16:50,515][INFO][idv.matt.controller.HelloController:29] - end
因為AOP將idv.matt.controller
下的所有方法都設為切入點,所以在執行HelloController.hello()
的先後執行LogAspect.before()
及LogAspect.after()
中的程式。
最後將修改推到github,請先參考使用SpringBoot打造記帳簿專案(十四)提交至Github
接著請看使用SpringBoot打造記帳簿專案(十七)使用Eclipse MyBatis Generator plugin自動產生存取資料表的檔案。
參考:
沒有留言:
張貼留言