springboot中怎么利用AOP統一處理web請求,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

成都創新互聯從2013年創立,先為無為等服務建站,無為等地企業,進行企業商務咨詢服務。為無為企業網站制作PC+手機+微官網三網同步一站式服務解決您的所有建站問題。
基本思想:
采用AOP的方式,攔截請求,寫入日志
AOP 是面向切面的編程,就是在運行期通過動態代理的方式對代碼進行增強處理
基于AOP不會破壞原來程序邏輯,因此它可以很好的對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
1.添加依賴
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-aop
引入spring-boot-starter-web 依賴之后無需在引入相關的日志依賴,spring-boot-starter-web中已經集成了slf4j 的依賴
引入spring-boot-starter-aop 依賴之后,AOP 的功能即是啟動狀態
2.配置
application.properties添加
# AOPspring.aop.auto=truespring.aop.proxy-target-class=true
logback-spring.xml,主要是ControllerRequest那部分
info ${CONSOLE_LOG_PATTERN} UTF-8 ${log.path}/debug/debug.log %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n UTF-8 ${log.path}/debug/debug-%d{yyyy-MM-dd}.%i.log 100MB 15 debug ACCEPT DENY ${log.path}/info/info.log %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n UTF-8 ${log.path}/info/info-%d{yyyy-MM-dd}.%i.log 100MB 15 info ACCEPT DENY ${log.path}/warn/warn.log %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n UTF-8 ${log.path}/warn/warn-%d{yyyy-MM-dd}.%i.log 100MB 15 warn ACCEPT DENY ${log.path}/error/error.log %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n UTF-8 ${log.path}/error/error-%d{yyyy-MM-dd}.%i.log 100MB 15 ERROR ACCEPT DENY ${log.path}/request/info.log ${log.path}/request/info.log.%d{yyyy-MM-dd} 30 %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
3..實現
實現切面的注解
(1)類注解
A. @Aspect 將一個java類定義為切面類
B. @order(i) 標記切面類的處理優先級,i值越小,優先級別越高。可以注解類,也能注解到方法上
(2)方法注解
A. @Pointcut 定義一個切入點,可以是一個表達式
execution表達式,eg:
任意公共方法的執行execution(public * *(..)) 任何一個以“set”開始的方法的執行execution(* set*(..)) 定義在controller包里的任意方法的執行execution(public * com.example.demo.controller.*(..)) 定義在controller包里的任意方法的執行execution(public * com.example.demo.controller.*.*(..)) 定義在controller包和所有子包里的任意類的任意方法的執行execution(public * com.example.demo.controller..*.*(..))
B. 實現在不同的位置切入
@Before 在切點前執行方法,內容為指定的切點 @After 在切點后,return前執行 @AfterReturning 切入點在 return內容之后(可用作處理返回值) @Around 切入點在前后切入內容,并自己控制何時執行切入的內容 @AfterThrowing 處理當切入部分拋出異常后的邏輯
C.@order(i) 標記切點的優先級,i越小,優先級越高
@order(i)注解說明
注解類,i值是,值越小,優先級越高
注解方法,分兩種情況
注解的是 @Before 是i值越小,優先級越高
注解的是 @After或@AfterReturning 中,i值越大,優先級越高
具體實現
package com.example.demo.configure;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.DefaultParameterNameDiscoverer;import org.springframework.core.ParameterNameDiscoverer;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.lang.reflect.Method;import java.net.InetAddress;import java.util.HashMap;import java.util.Map;@Aspect@Componentpublic class WebRequestLogAspect { private final Logger loggerController = LoggerFactory.getLogger("ControllerRequest"); private final Logger logger = LoggerFactory.getLogger(WebRequestLogAspect.class); ThreadLocal startTime = new ThreadLocal<>(); ThreadLocal beanName = new ThreadLocal<>(); ThreadLocal user = new ThreadLocal<>(); ThreadLocal methodName = new ThreadLocal<>(); ThreadLocal params = new ThreadLocal<>(); ThreadLocal remoteAddr = new ThreadLocal<>(); ThreadLocal uri = new ThreadLocal<>(); private static Map getFieldsName(ProceedingJoinPoint joinPoint) { // 參數值 Object[] args = joinPoint.getArgs(); ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); String[] parameterNames = pnd.getParameterNames(method); Map paramMap = new HashMap<>(32); for (int i = 0; i < parameterNames.length; i++) { paramMap.put(parameterNames[i], args[i] + "(" + args[i].getClass().getSimpleName() + ")"); } return paramMap; } @Pointcut("execution(public * com.example.demo.controller..*.*(..))") public void webRequestLog() { } /** * 前置通知,方法調用前被調用 * @param joinPoint */ @Before("webRequestLog()") public void doBefore(JoinPoint joinPoint) { try { startTime.set(System.currentTimeMillis()); // 接收到請求,記錄請求內容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); beanName.set(joinPoint.getSignature().getDeclaringTypeName()); methodName.set(joinPoint.getSignature().getName()); uri.set(request.getRequestURI()); remoteAddr.set(getIpAddr(request)); user.set((String) request.getSession().getAttribute("user")); } catch (Exception e) { logger.error("***操作請求日志記錄失敗doBefore()***", e); } } /** * 環繞通知,環繞增強,相當于MethodInterceptor * @param thisJoinPoint */ @Around("webRequestLog()") public Object proceed(ProceedingJoinPoint thisJoinPoint) throws Throwable { Object object = thisJoinPoint.proceed(); Map fieldsName = getFieldsName(thisJoinPoint); params.set(fieldsName.toString()); return object; } /** * 處理完請求返回內容 * @param result */ @AfterReturning(returning = "result", pointcut = "webRequestLog()") public void doAfterReturning(Object result) { try { long requestTime = (System.currentTimeMillis() - startTime.get()) / 1000; loggerController.info("請求耗時:" + requestTime + ", uri=" + uri.get() + "; beanName=" + beanName.get() + "; remoteAddr=" + remoteAddr.get() + "; user=" + user.get() + "; methodName=" + methodName.get() + "; params=" + params.get() + "; RESPONSE : " + result); } catch (Exception e) { logger.error("***操作請求日志記錄失敗doAfterReturning()***", e); } } /** * 獲取登錄用戶遠程主機ip地址 * * @param request * @return */ private String getIpAddr(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(); if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) { //根據網卡取本機配置的IP InetAddress inet = null; try { inet = InetAddress.getLocalHost(); } catch (Exception e) { e.printStackTrace(); } ip = inet.getHostAddress(); } } // 多個代理的情況,第一個IP為客戶端真實IP,多個IP按照','分割 if (ip != null && ip.length() > 15) { if (ip.indexOf(",") > 0) { ip = ip.substring(0, ip.indexOf(",")); } } return ip; }}
4.測試類
package com.example.demo.controller;import com.alibaba.fastjson.JSONObject;import com.example.demo.dao.UserRepository;import com.example.demo.domain.User;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;import java.util.Map;@RestControllerpublic class Demo { @RequestMapping (value = "test1") public String test1(@RequestParam(defaultValue = "0") Integer id,@RequestParam(defaultValue = "0")String name){ return id+name; } @RequestMapping("hello") public String hello() { return "Hello World!"; } @PostMapping("/updateStatus") public Object updateStatus(@RequestBody JSONObject jsonParam) { return jsonParam; }}
輸出到logs/request/info.log內容
2019-09-11 13:31:45.729 [http-nio-8080-exec-4] INFO ControllerRequest - 請求耗時:0, uri=/test1; beanName=com.example.demo.controller.Demo; remoteAddr=172.27.0.17; user=null; methodName=test1; params={name=abcdef(String), id=123(Integer)}; RESPONSE : 123abcdef2019-09-11 13:32:16.692 [http-nio-8080-exec-5] INFO ControllerRequest - 請求耗時:0, uri=/updateStatus; beanName=com.example.demo.controller.Demo; remoteAddr=172.27.0.17; user=null; methodName=updateStatus; params={jsonParam={"id":"17","type":3,"status":2}(JSONObject)}; RESPONSE : {"id":"17","type":3,"status":2}2019-09-11 13:33:32.584 [http-nio-8080-exec-7] INFO ControllerRequest - 請求耗時:0, uri=/hello; beanName=com.example.demo.controller.Demo; remoteAddr=172.27.0.17; user=null; methodName=hello; params={}; RESPONSE : Hello World!
看完上述內容,你們掌握springboot中怎么利用AOP統一處理web請求的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注創新互聯行業資訊頻道,感謝各位的閱讀!
標題名稱:springboot中怎么利用AOP統一處理web請求
文章位置:
http://m.jcarcd.cn/article/jsohgi.html