一、AOP簡介
AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是Spring框架中的一個重要內容,
它通過對既有程序定義一個切入點,然后在其前后切入不同的執行內容,比如常見的有:打開數據庫連接/關閉數據庫連接、打開事務/關閉事務、記錄日志等。基於AOP不會破壞原
來程序邏輯,因此它可以很好的對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
下面主要講兩個內容,一個是如何在Spring Boot中引入Aop功能,二是如何使用Aop做切面去統一處理Web請求的日志。
二、引入jar包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
三、實現Web層的日志切面
而當我們需要使用CGLIB來實現AOP的時候,需要配置spring.aop.proxy-target-class=true,不然默認使用的是標准Java的實現。
實現AOP的切面主要有以下幾個要素:
使用@Aspect注解將一個java類定義為切面類
使用@Pointcut定義一個切入點,可以是一個規則表達式,比如下例中某個package下的所有函數,也可以是一個注解等。
根據需要在切入點不同位置的切入內容
使用@Before在切入點開始處切入內容
使用@After在切入點結尾處切入內容
使用@AfterReturning在切入點return內容之后切入內容(可以用來對處理返回值做一些加工處理)
使用@Around在切入點前后切入內容,並自己控制何時執行切入點自身的內容
使用@AfterThrowing用來處理當切入內容部分拋出異常之后的處理邏輯
import java.util.Arrays; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; 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; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; /** * 項目日志 切面 * @author zengdq * 2019年1月5日 */ @Aspect @Component public class WebLogAspect { private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class); private static final String PLATFORM_NAME = "xxx項目名稱"; /** * 統計時間 */ ThreadLocal<Long> startTime = new ThreadLocal<>(); @Pointcut("execution(public * com.web.controller..*.*(..))") public void webLog(){} @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { startTime.set(System.currentTimeMillis()); // 接收到請求,記錄請求內容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //打印請求參數 Map<String, String[]> paramMap = request.getParameterMap(); if(paramMap != null && paramMap.size() > 0) { StringBuffer paramSbf = new StringBuffer(); for(String mapKey:paramMap.keySet()) { String[] mapValue = paramMap.get(mapKey); //添加判斷 if(mapValue != null && mapValue.length > 0) { for(String paramStr:mapValue) { if(StringUtils.isNotBlank(paramStr)) { paramSbf.append("參數"+mapKey+"="); paramSbf.append(paramStr); paramSbf.append(";"); } } }//END if }//END for //打印日志參數 logger.info(PLATFORM_NAME+"-->request請求參數PARAM : " + paramSbf); }//END if // 記錄下請求內容 logger.info(PLATFORM_NAME+"-->request請求URL : " + request.getRequestURL().toString()); logger.info(PLATFORM_NAME+"-->request請求方法HTTP_METHOD : " + request.getMethod()); logger.info(PLATFORM_NAME+"-->request請求方法IP : " + getIP(request)); logger.info(PLATFORM_NAME+"-->request請求類方法CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info(PLATFORM_NAME+"-->request請求ARGS : " + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { // 處理完請求,返回內容 logger.info(PLATFORM_NAME+"-->response請求響應結果RESULT: " + ret); logger.info(PLATFORM_NAME+"-->response請求響應時間= 【" + (System.currentTimeMillis() - startTime.get())+"】毫秒"); } /** * * @param request * @return */ private static String getIP(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } }
有時會使用到反射
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; import java.lang.reflect.Method; @AfterReturning( pointcut = "log()" ) public void addLog( JoinPoint joinPoint ){ // 從切面織入點通過反射獲取織入點的方法 MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature(); // 獲取切入點的方法 Method method = methodSignature.getMethod(); RequestMapping mapping = method.getAnnotation(RequestMapping.calss); String methodMapping = mapping.value()[0]; // 獲取請求的類映射路徑 String className = joinPoint.getTarget().getClass().getAnnotation(RequestMapping.class).value()[0]; // 業務操作 }
參考 : https://blog.csdn.net/zengdeqing2012/article/details/86076217