Spring Boot 之AOP實戰(日誌元件)
阿新 • • 發佈:2019-01-23
AOP 面向切面程式設計
一、相關概念
Aspect:切面
PointCut:切點
Advisor:通知
共有五種通知:
Before:前置通知
After:後置通知
Around:環繞通知(功能最強大)
AfterReturing:返回通知 (沒有異常的時候回執行)
AfterThrowing:異常通知 (有異常的時候回執行)
二、應用
新建Spring boot 專案 pom.xml檔案引入 spring-boot-starter-aop ,spring-boot-starter-web依賴<?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> <groupId>com.example.beanValidation</groupId> <artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId><version>1.5.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </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> </project>
建立一個切面WebLogAspect
package com.example.beanvalidation.aop; import com.example.beanvalidation.exception.WrapException; import com.fasterxml.jackson.databind.ObjectMapper; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; /** * Created by sai.luo on 2017-9-19. */ @Component @Aspect public class WebLogAspect { private static final Logger log = LoggerFactory.getLogger(WebLogAspect.class); private static final ObjectMapper objectMapper = new ObjectMapper(); /** * 定義切點 */ @Pointcut("execution(* com.example.beanvalidation.controller..*.*(..))") public void webLog(){} /** * 定義前置通知 */ @Before("webLog()") public void before(JoinPoint joinPoint){ log.info("前置通知執行"); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); //記錄日誌 String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); log.info("請求方法:"+className+"."+methodName); log.info("請求引數:"+ Arrays.asList(joinPoint.getArgs()[0])); log.info("IP:"+request.getRemoteAddr()); } /** * 返回通知 不管是拋異常還是正常都是會執行 */ @After("webLog()") public void after(){ log.info("後置通知執行"); } /** * 定義環繞通知 方法簽名有返回型別 */ @Around("webLog()") public Object around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{ log.info("環繞通知開始"); // 實際開發中需要定義一個返回類,包裝返回結果。 /** * public class ResponseResult{ * int code ; * String message ; * T result ; * * // get set methods * // constructor * } */ try { Object proceed = proceedingJoinPoint.proceed(); log.info("返回正常結果:"+objectMapper.writeValueAsString(proceed)); return proceed; }catch (WrapException ex){ log.error("出現異常:"+ex.getMessage()); } log.info("環繞通知結束"); return null; } /** * 異常通知 不建議使用異常通知,因為會使用系統輸出列印日誌,消耗系統資源,建議在環繞通知中 捕獲不同異常,進行處理 */ @AfterThrowing(pointcut = "webLog()",throwing = "ex") public void afterThrowing(Throwable ex){ log.info("異常通知"); log.error("error: "+ex.getMessage()); } /** * 正常返回通知 */ @AfterReturning(pointcut = "webLog()",returning = "result") public void afterReturning(Object result)throws Exception{ log.info("正常返回通知"); log.info(objectMapper.writeValueAsString(result)); } }
如果切面類WenLogAspect不在main啟動類所在的包或者子包中,則需要手動注入到spring 容器中,才能生效