spring AOP實現請求日誌記錄功能

在實際開發中,對於外部調用接口時,要記錄每一次請求的參數,以及應用響應內容給調用者。這時候,咱們能夠採用AOP技術針對指定的請求,指定的方法進行攔截記錄日誌。java

首先定義AOP切面類,代碼以下web

/**
 * @ClassName:  SupplierInvokeLogAspect   
 * @Description: 供應商調用商城日誌管理
 * @author: kejie.huang
 * @date:   2019年4月26日 上午8:58:01   
 *
 */
@Aspect
@Component
public class SupplierInvokeLogAspect {
  
  @GuardedBy("itself")
  @Reference(version = IfSupplierInvokeLogService.LATEST_VERSION)
  private IfSupplierInvokeLogService ifSupplierInvokeLogService;
  
  private String splitStr = "-";
  private String formatStr =
      WebUtil.generateStrSplit(splitStr, 10) + "%s" + WebUtil.generateStrSplit(splitStr, 10);

  public SupplierInvokeLogAspect() {
    WebUtil.getLogger().info((String.format(formatStr, "aopLogAspect加載")));
  }
  //定義攔截controller.invoke下面子包全部的類全部方法,參數隨意
  @Pointcut("execution(* com.csair.csm.web.controller.invoke..*.*(..))")
  private void pointcut() {}

  @Before("pointcut()")
  public void before() {
    WebUtil.getLogger().info((String.format(formatStr, "before加載")));
  }

  private Map<String, Object> getMethodInfo(ProceedingJoinPoint pjp)
      throws NoSuchMethodException, SecurityException {
    Map<String, Object> map = new HashMap<String, Object>();

    Object targetObject = this.getTargetObject(pjp);
    String targetMethodName = pjp.getSignature().getName();
    Object[] targetArgs = pjp.getArgs();
    List<Object> argList = new ArrayList<Object>();
    for (Object arg : targetArgs) {
      if (WebUtil.isValid(arg)) {
        Annotation[] annotations = arg.getClass().getAnnotations();
        for (Annotation annotation : annotations) {
          if (annotation.annotationType().isAssignableFrom(SupplierInvokeLog.class)) {
            argList.add(arg);
          }
        }
      }
    }
    MethodSignature targetMethodSignature = (MethodSignature) pjp.getSignature();
    Class<?>[] targetParameterTypes = targetMethodSignature.getMethod().getParameterTypes();
    map.put("method", targetObject.getClass().getMethod(targetMethodName, targetParameterTypes));
    map.put("argList", argList);

    return map;
  }

  private Object getTargetObject(ProceedingJoinPoint pjp) {
    return pjp.getTarget();
  }


  @Around("pointcut()")
  public Object around(ProceedingJoinPoint pjp) throws Throwable {

    WebUtil.getLogger().info((String.format(formatStr, "開始")));
    HttpServletRequest httpRequest = WebUtil.getHttpRequest();

    String httpRequestInfo = WebUtil.getHttpRequestInfo(httpRequest);

    String method = null;
    Map<String, Object> beginTimeInfo = WebUtil.getCurrentTimeInfo();
    long startLong = (long) beginTimeInfo.get("currentTimeLong");

    Object returnObject = pjp.proceed();

    Map<String, Object> endTimeInfo = WebUtil.getCurrentTimeInfo();
    long endLong = (long) endTimeInfo.get("currentTimeLong");
    long intervalLong = endLong - startLong;
    Map<String, Object> methodInfo = this.getMethodInfo(pjp);
    Method targetMethod = (Method) methodInfo.get("method");
    if (WebUtil.checkIsAssignAnnotaion(targetMethod, SupplierInvokeLog.class)) {
      SupplierInvokeLog supplierInvokeLog = targetMethod.getAnnotation(SupplierInvokeLog.class);
      method = supplierInvokeLog.method();
      String requestId = ((IfSupplierInvokeCommonResponse)returnObject).getRequestId();
      
      ifSupplierInvokeLogService.addRequestLog(method, requestId, httpRequestInfo, WebUtil.beanToJson(returnObject), Integer.valueOf(String.valueOf(intervalLong)));
    }
    return returnObject;
  }

}

 

定義註解標註在須要攔截的方法上spring

/**
 * @ClassName:  SupplierInvokeLog   
 * @Description:供應商調用訪問註解  
 * @author: kejie.huang
 * @date:   2019年4月26日 上午8:37:21   
 *
 */
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SupplierInvokeLog {
  String module() default "";

  String method() default "";
}
/**
   * @Title: getTokenByParams   
   * @Description: 商品新增以及訂單消息入口
   * @param: @param httpServletRequest
   * @param: @param requestTokenParams
   * @param: @return      
   * @return: IfSupplierTokenResponse 
   * @author: kejie.huang
   * @throws
   */
  @RequestMapping(value = "/message/handle", method = RequestMethod.POST, produces = "application/json;charset=utf-8")
  @ResponseBody
  @SupplierInvokeLog(method = "SupplierMessageController.changeProdOrderMessage()")
  public IfSupplierInvokeCommonResponse changeProdOrderMessage(HttpServletRequest httpServletRequest,@RequestBody(required = false) RequestMessageParams requestMessageParams){
    IfSupplierInvokeCommonResponse ifSupplierInvokeCommonResponse = new IfSupplierInvokeCommonResponse();
    String reqToken = httpServletRequest.getHeader("token"); // 請求Token
    String requestId = WebUtil.getRandomUUID(); //請求ID
    try {
      ifSupplierInvokeCommonResponse = getService().addMessageByParams(reqToken, requestMessageParams,requestId);
      return ifSupplierInvokeCommonResponse;
    } catch (Exception error) {
      ifSupplierInvokeCommonResponse.setResultCode(MessageCode.CODE_500);
      ifSupplierInvokeCommonResponse.setResultMsg("商品訂單消息處理失敗");
      ifSupplierInvokeCommonResponse.setSuccess(false);
      ifSupplierInvokeCommonResponse.setRequestId(requestId);
      error.printStackTrace();
    }
    return ifSupplierInvokeCommonResponse;
  }

定義響應實體類IfSupplierInvokeCommonResponsejson

/**
 * @ClassName:  IfSupplierInvokeResponse   
 * @Description:響應公用類
 * @author: kejie.huang
 * @param <E>
 * @date:   2019年4月11日 下午2:28:11   
 *
 */
public class IfSupplierInvokeCommonResponse implements Serializable{

  /**   
   * @Fields serialVersionUID   
   */
  private static final long serialVersionUID = 5523382471126518572L;
  
  /**
   * 是否成功
   */
  private boolean success;
  /**
   * 響應信息
   */
  private String resultMsg;
  /**
   * 響應碼
   */
  private Integer resultCode;
  /**
   * 請求ID
   */
  private String requestId;
  public boolean getSuccess() {
    return success;
  }

  public void setSuccess(boolean success) {
    this.success = success;
  }

  public String getResultMsg() {
    return resultMsg;
  }

  public void setResultMsg(String resultMsg) {
    this.resultMsg = resultMsg;
  }

  public Integer getResultCode() {
    return resultCode;
  }

  public void setResultCode(Integer resultCode) {
    this.resultCode = resultCode;
  }

  public String getRequestId() {
    return requestId;
  }

  public void setRequestId(String requestId) {
    this.requestId = requestId;
  }

  public IfSupplierInvokeCommonResponse() {
    super();
  }

  public IfSupplierInvokeCommonResponse(boolean success, String resultMsg, Integer resultCode,
      String requestId) {
    super();
    this.success = success;
    this.resultMsg = resultMsg;
    this.resultCode = resultCode;
    this.requestId = requestId;
  }

  @Override
  public String toString() {
    return "BaseIfSupplierInvokeResponse [success=" + success + ", resultMsg=" + resultMsg
        + ", resultCode=" + resultCode + ", requestId=" + requestId + "]";
  }
}

這時候AOP的功能已經搭建好了,可是在調用接口時,會發現獲取AOP響應響應的內容是爲空的,主要是springmvc請求在接口時,已經關閉流了,然而在AOP再次獲取的時候,已經獲取不了,解決方案,把當前流的內容傳遞下去,能夠定義過濾器filter實現mvc

/**
 * @ClassName: RequestParamFilter
 * @Description: 請求參數過濾器
 * @author: kejie.huang
 * @date: 2019年4月26日 上午8:46:26
 * 
 */
public class RequestParamFilter implements Filter {
  
  
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {

  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    chain.doFilter(new WrapperRequestBody(httpServletRequest), response);
  }

  @Override
  public void destroy() {

  }

}
/**
 * @ClassName: WrapperRequestBody
 * @Description: 重寫HttpServletRequestWrapper,可用於流傳下去,由於springmvc流獲取後就關閉,致使aop獲取請求內容爲空 而且實現XSS過濾
 * @author: kejie.huang
 * @date: 2019年5月15日 下午4:56:37
 * 
 */
public class WrapperRequestBody extends HttpServletRequestWrapper {

  private final String body;

  public WrapperRequestBody(HttpServletRequest request) throws IOException {
    super(request);
    StringBuilder stringBuilder = new StringBuilder();
    BufferedReader bufferedReader = null;
    try {
      InputStream inputStream = request.getInputStream();
      if (inputStream != null) {
        bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        char[] charBuffer = new char[128];
        int bytesRead = -1;
        while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
          stringBuilder.append(charBuffer, 0, bytesRead);
        }
      } else {
        stringBuilder.append("");
      }
    } catch (IOException ex) {
      throw ex;
    } finally {
      if (bufferedReader != null) {
        try {
          bufferedReader.close();
        } catch (IOException ex) {
          ex.printStackTrace();
        }
      }
    }
    body = format(stringBuilder.toString());
  }

  @Override
  public ServletInputStream getInputStream() throws IOException {
    final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
    ServletInputStream servletInputStream = new ServletInputStream() {
      public int read() throws IOException {
        return byteArrayInputStream.read();
      }
    };
    return servletInputStream;
  }

  @Override
  public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(this.getInputStream()));
  }

  public String getBody() {
    return this.body;
  }
}

最後一步則在web.xml定義過濾器指向咱們的過濾器類便可。app

<filter>
        <filter-name>requestParamFilter</filter-name>
        <filter-class>com.csair.csm.web.filter.RequestParamFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>requestParamFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
相關文章
相關標籤/搜索