spring AOP实现请求日志记录功能

在实际开发中,对于外部调用接口时,要记录每一次请求的参数,以及应用响应内容给调用者。这时候,我们可以采用AOP技术针对指定的请求,指定的方法进行拦截记录日志。

首先定义AOP切面类,代码如下

/**
 * @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;
  }

}

 

定义注解标注在需要拦截的方法上

/**
 * @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;
  }

定义响应实体类IfSupplierInvokeCommonResponse

/**
 * @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实现

/**
 * @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定义过滤器指向我们的过滤器类即可。

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

 

转载于:https://my.oschina.net/u/3155476/blog/3055448

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值