在实际开发中,对于外部调用接口时,要记录每一次请求的参数,以及应用响应内容给调用者。这时候,我们可以采用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 getMethodInfo(ProceedingJoinPoint pjp)
throws NoSuchMethodException, SecurityException {
Map map = new HashMap();
Object targetObject = this.getTargetObject(pjp);
String targetMethodName = pjp.getSignature().getName();
Object[] targetArgs = pjp.getArgs();
List
定义注解标注在需要拦截的方法上
/**
* @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
* @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定义过滤器指向我们的过滤器类即可。
requestParamFilter
com.csair.csm.web.filter.RequestParamFilter
requestParamFilter
/*