本示例爲一個基於註解的切面編程實踐,該切面功能:主要是用來統計被註解標識的方法執行時的耗時時長
1,首先 配置maven依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
2,其次定義一個註解
package com.example.interceptor.aop;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TimeLog {
String value() default "";
}
3,再定義一個基於註解的切面及編寫相應的前置、後置增強方法
package com.example.interceptor.aop;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 監控日誌攔切面截器
* @author weichangzhong
* @Aspect 作用是把當前類標識爲一個切面供容器讀取
*/
@Slf4j
@Aspect
@Component
@Order(1)
public class LogAspect {
private ThreadLocal<Long> startTime = new ThreadLocal<>();
/**
* 切入點 JoinPoint的集合,是程序中需要注入Advice的位置的集合,指明Advice要在什麼樣的條件下才能被觸發,在程序中主要體現爲書寫切入點表達式
* 由下列方式來定義或者通過 &&、 ||、 !、 的方式進行組合, 如:
* execution:用於匹配方法執行的連接點
* @annotation:用於匹配當前執行方法持有指定註解的方法
*/
@Pointcut("@annotation(com.example.interceptor.aop.TimeLog)")
public void logPointCut() {
}
/**
* 標識一個前置增強方法,相當於BeforeAdvice的功能. 在切點方法之前執行
*/
@Before("logPointCut()")
public void before() {
log.info("--------------before----------logPointCut");
startTime.set(System.currentTimeMillis());
}
/**
* 後置增強,似於AfterReturningAdvice, 方法返回後、正常退出時執行
* returning的值是對應的是業務方法 的返回值,且其名稱要與afterReturning方法的參數名相同
*/
@AfterReturning(pointcut = "logPointCut()", returning = "response")
public void afterReturning(JoinPoint joinPoint, Object response) {
Long cost = System.currentTimeMillis() - startTime.get();
log.info("--------------afterReturn--------logPointCut, time: {}", cost);
//獲取方法類型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String className = joinPoint.getTarget().getClass().getName();
TimeLog annotation = method.getAnnotation(TimeLog.class);
if(annotation == null) {
log.info("ClassName: {}, methodName: {}", className, method.getName());
}else {
log.info("The value of annotation is: {}, className: {}, methodName: {}", annotation.value(), className, method.getName());
}
//獲取請求參數
Object[] args = joinPoint.getArgs();
if (ArrayUtils.isNotEmpty(args)) {
//httpServletRequest、httpServletResponse、multipartFile的參數爲異步組裝的,不能序列化,因此需要過濾掉
List<Object> filterArg = Arrays.stream(args).filter(
arg -> !(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse) && !(arg instanceof MultipartFile)
).collect(Collectors.toList());
log.info("The parameter is: {}", filterArg.toString());
}else{
log.info("The parameter is: {}", args);
}
}
/**
* 異常拋出增強,相當於ThrowsAdvice. 切點方法拋異常執行
* @param joinPoint
* @param ex
*/
@AfterThrowing(pointcut = "logPointCut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
Long cost = System.currentTimeMillis() - startTime.get();
if (log.isDebugEnabled()) {
log.debug("----------afterThrow--------logPointCut, time: {}", cost);
}
}
}
3,最後定義一個被攔截的controller方法
package com.example.interceptor.controller;
import com.example.interceptor.aop.TimeLog;
import com.example.interceptor.service.TestService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author weichangzhong
*/
@RestController
@RequestMapping("/test")
public class TestController {
@Resource
private TestService testService;
@GetMapping
@TimeLog("test-1")
public String getTest(@RequestParam String name, String email) {
return testService.getTestString();
}
}
service層:
package com.example.interceptor.service;
import com.example.interceptor.aop.TimeLog;
import org.springframework.stereotype.Service;
/**
* @author weichangzhong
*/
@Service
public class TestService {
@TimeLog
public String getTestString() {
return "test response";
}
}
啓動服務後,調用接口:localhost:8080/test?name=me&email=emailTest