在實際開發中,對於外部調用接口時,要記錄每一次請求的參數,以及應用響應內容給調用者。這時候,咱們能夠採用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>