中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

springboot中怎么利用AOP統一處理web請求

發布時間:2021-07-23 17:00:07 來源:億速云 閱讀:141 作者:Leah 欄目:編程語言

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

基本思想:

采用AOP的方式,攔截請求,寫入日志

AOP 是面向切面的編程,就是在運行期通過動態代理的方式對代碼進行增強處理

基于AOP不會破壞原來程序邏輯,因此它可以很好的對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。

1.添加依賴

<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>

引入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那部分

<?xml version="1.0" encoding="UTF-8"?><configuration scan="true" scanPeriod="60 seconds" debug="false">  <property name="log.path" value="logs" />  <!--0. 日志格式和顏色渲染 -->  <!-- 彩色日志依賴的渲染類 -->  <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />  <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />  <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />  <!-- 彩色日志格式 -->  <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>  <!--1. 輸出到控制臺-->  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">    <!--此日志appender是為開發使用,只配置最底級別,控制臺輸出的日志級別是大于或等于此級別的日志信息-->    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">      <level>info</level>    </filter>    <encoder>      <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>      <!-- 設置字符集 -->      <charset>UTF-8</charset>    </encoder>  </appender>  <!--2. 輸出到文檔-->  <!-- 2.1 level為 DEBUG 日志,時間滾動輸出 -->  <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">    <!-- 正在記錄的日志文檔的路徑及文檔名 -->    <file>${log.path}/debug/debug.log</file>    <!--日志文檔輸出格式-->    <encoder>      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>      <charset>UTF-8</charset> <!-- 設置字符集 -->    </encoder>    <!-- 日志記錄器的滾動策略,按日期,按大小記錄 -->    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">      <!-- 日志歸檔 -->      <fileNamePattern>${log.path}/debug/debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>      <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">        <maxFileSize>100MB</maxFileSize>      </timeBasedFileNamingAndTriggeringPolicy>      <!--日志文檔保留天數-->      <maxHistory>15</maxHistory>    </rollingPolicy>    <!-- 此日志文檔只記錄debug級別的 -->    <filter class="ch.qos.logback.classic.filter.LevelFilter">      <level>debug</level>      <onMatch>ACCEPT</onMatch>      <onMismatch>DENY</onMismatch>    </filter>  </appender>  <!-- 2.2 level為 INFO 日志,時間滾動輸出 -->  <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">    <!-- 正在記錄的日志文檔的路徑及文檔名 -->    <file>${log.path}/info/info.log</file>    <!--日志文檔輸出格式-->    <encoder>      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>      <charset>UTF-8</charset>    </encoder>    <!-- 日志記錄器的滾動策略,按日期,按大小記錄 -->    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">      <!-- 每天日志歸檔路徑以及格式 -->      <fileNamePattern>${log.path}/info/info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>      <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">        <maxFileSize>100MB</maxFileSize>      </timeBasedFileNamingAndTriggeringPolicy>      <!--日志文檔保留天數-->      <maxHistory>15</maxHistory>    </rollingPolicy>    <!-- 此日志文檔只記錄info級別的 -->    <filter class="ch.qos.logback.classic.filter.LevelFilter">      <level>info</level>      <onMatch>ACCEPT</onMatch>      <onMismatch>DENY</onMismatch>    </filter>  </appender>  <!-- 2.3 level為 WARN 日志,時間滾動輸出 -->  <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">    <!-- 正在記錄的日志文檔的路徑及文檔名 -->    <file>${log.path}/warn/warn.log</file>    <!--日志文檔輸出格式-->    <encoder>      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>      <charset>UTF-8</charset> <!-- 此處設置字符集 -->    </encoder>    <!-- 日志記錄器的滾動策略,按日期,按大小記錄 -->    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">      <fileNamePattern>${log.path}/warn/warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>      <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">        <maxFileSize>100MB</maxFileSize>      </timeBasedFileNamingAndTriggeringPolicy>      <!--日志文檔保留天數-->      <maxHistory>15</maxHistory>    </rollingPolicy>    <!-- 此日志文檔只記錄warn級別的 -->    <filter class="ch.qos.logback.classic.filter.LevelFilter">      <level>warn</level>      <onMatch>ACCEPT</onMatch>      <onMismatch>DENY</onMismatch>    </filter>  </appender>  <!-- 2.4 level為 ERROR 日志,時間滾動輸出 -->  <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">    <!-- 正在記錄的日志文檔的路徑及文檔名 -->    <file>${log.path}/error/error.log</file>    <!--日志文檔輸出格式-->    <encoder>      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>      <charset>UTF-8</charset> <!-- 此處設置字符集 -->    </encoder>    <!-- 日志記錄器的滾動策略,按日期,按大小記錄 -->    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">      <fileNamePattern>${log.path}/error/error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>      <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">        <maxFileSize>100MB</maxFileSize>      </timeBasedFileNamingAndTriggeringPolicy>      <!--日志文檔保留天數-->      <maxHistory>15</maxHistory>    </rollingPolicy>    <!-- 此日志文檔只記錄ERROR級別的 -->    <filter class="ch.qos.logback.classic.filter.LevelFilter">      <level>ERROR</level>      <onMatch>ACCEPT</onMatch>      <onMismatch>DENY</onMismatch>    </filter>  </appender>  <springProfile name="dev">    <root level="info">      <appender-ref ref="CONSOLE" />      <appender-ref ref="DEBUG_FILE" />      <appender-ref ref="INFO_FILE" />      <appender-ref ref="WARN_FILE" />      <appender-ref ref="ERROR_FILE" />    </root>  </springProfile>  <springProfile name="prod">    <root level="info">      <appender-ref ref="DEBUG_FILE" />      <appender-ref ref="INFO_FILE" />      <appender-ref ref="WARN_FILE" />      <appender-ref ref="ERROR_FILE" />    </root>  </springProfile>  <appender name="ControllerRequest" class="ch.qos.logback.core.rolling.RollingFileAppender">    <file>${log.path}/request/info.log</file>    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">      <FileNamePattern>${log.path}/request/info.log.%d{yyyy-MM-dd}</FileNamePattern>      <maxHistory>30</maxHistory>    </rollingPolicy>    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>    </encoder>  </appender>  <logger name="ControllerRequest" level="DEBUG" additivity="false">    <appender-ref ref="ControllerRequest"/>  </logger></configuration>

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<Long> startTime = new ThreadLocal<>();  ThreadLocal<String> beanName = new ThreadLocal<>();  ThreadLocal<String> user = new ThreadLocal<>();  ThreadLocal<String> methodName = new ThreadLocal<>();  ThreadLocal<String> params = new ThreadLocal<>();  ThreadLocal<String> remoteAddr = new ThreadLocal<>();  ThreadLocal<String> uri = new ThreadLocal<>();  private static Map<String, Object> 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<String, Object> 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<String, Object> 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請求的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

肥西县| 肥乡县| 通化市| 南汇区| 达拉特旗| 察哈| 那曲县| 长沙县| 射阳县| 法库县| 乡城县| 蛟河市| 夹江县| 台湾省| 广饶县| 凤山县| 武定县| 崇文区| 鄂托克旗| 林口县| 公安县| 灵宝市| 泸定县| 大邑县| 呈贡县| 思茅市| 阳谷县| 辉南县| 博乐市| 常宁市| 霍林郭勒市| 日土县| 沾化县| 万全县| 博白县| 晋中市| 拜城县| 营口市| 县级市| 永安市| 礼泉县|