aop 打印请求信息
execution()介绍
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体,可以扫描控制层的接口、某个注解、或者其他需要扫描的类。
2、第一个*号:表示返回类型,*号表示所有的类型,比如public,protect,private等。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.demo.service.impl包、子孙包下所有类的方法。
4、第二个*号:表示子包名,*号表示所有子包。
5、第三个*号:表示类名,*号表示所有子包下的类。
6、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
package com.pacific.rsp.port.manager.aspect; 2 3 import com.alibaba.fastjson.JSON; 4 import com.alibaba.fastjson.JSONObject; 5 import org.apache.commons.lang.ArrayUtils; 6 import org.aspectj.lang.JoinPoint; 7 import org.aspectj.lang.Signature; 8 import org.aspectj.lang.annotation.*; 9 import org.aspectj.lang.reflect.MethodSignature; 10 import org.slf4j.Logger; 11 import org.slf4j.LoggerFactory; 12 import org.springframework.stereotype.Component; 13 import org.springframework.web.context.request.RequestContextHolder; 14 import org.springframework.web.context.request.ServletRequestAttributes; 15 import org.springframework.web.multipart.MultipartFile; 16 17 import javax.servlet.http.HttpServletRequest; 18 import javax.servlet.http.HttpServletResponse; 19 import java.util.Arrays; 20 import java.util.List; 21 import java.util.stream.Collectors; 22 import java.util.stream.Stream; 23 24 25 /** 26 * @Description: aop 打印日志 27 * @Author: mingtian 28 * @CreateDate: 2020/5/11 15:52 29 * @Version: 1.0 30 */ 31 @Aspect 32 @Component 33 public class HttpAspect { 34 /** 35 * 打印日志 36 */ 37 protected Logger logger = LoggerFactory.getLogger(HttpAspect.class); 38 39 /** 40 * 跨行 41 */ 42 private static final String LINE_SEPARATOR = System.lineSeparator(); 43 44 /** 45 * 要处理的方法,包名+类名+方法名 46 */ 47 @Pointcut("execution(* com.example.web.controller..*.*(..))") 48 public void cut() { 49 } 50 51 52 /** 53 * //在调用上面 @Pointcut标注的方法前执行以下方法 54 * 55 * @param joinPoint 56 */ 57 @Before("cut()") 58 public void doBefore(JoinPoint joinPoint) { 59 logger.info("------------------------ Start ----------------------------"); 60 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 61 HttpServletRequest request = attributes.getRequest(); 62 Signature signature = joinPoint.getSignature(); 63 // 记录下请求内容 64 logger.info("Request MethodType:{}", request.getMethod()); 65 logger.info("Request IP:{}", request.getRemoteAddr()); 66 logger.info("Request Url:{}", request.getRequestURL().toString()); 67 logger.info("Request Class and Methods:{}.{}", signature.getDeclaringTypeName(), signature.getName()); 68 boolean result = checkParamType(signature); 69 if (result) { 70 logger.info("文件格式不打印参数信息"); 71 return; 72 } 73 Object[] args = joinPoint.getArgs(); 74 //序列化时过滤掉request和response 75 List<Object> logArgs = streamOf(args).filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse))) 76 .collect(Collectors.toList()); 77 String argStr = JSON.toJSONString(logArgs); 78 logger.info("Request Args:{}", argStr); 79 } 80 81 /** 82 * 判断参数类型是否是文件 83 * 84 * @param signature 85 * @return 86 */ 87 public boolean checkParamType(Signature signature) { 88 MethodSignature methodSignature = (MethodSignature) signature; 89 Class[] parameterTypes = methodSignature.getParameterTypes(); 90 logger.info("parameterTypes:{}", parameterTypes); 91 for (Class cla : parameterTypes) { 92 if (isFile(cla)) { 93 return true; 94 } 95 } 96 return false; 97 } 98 99 /** 100 * 过滤工具类 101 * 102 * @param array 103 * @param <T> 104 * @return 105 */ 106 public static <T> Stream<T> streamOf(T[] array) { 107 return ArrayUtils.isEmpty(array) ? Stream.empty() : Arrays.asList(array).stream(); 108 } 109 110 /** 111 * 调用方法以何种方式结束,都会执行 112 */ 113 @After("cut()") 114 public void doAfter() { 115 116 } 117 118 /** 119 * //在调用上面 @Pointcut标注的方法后执行。用于获取返回值 120 * 121 * @param obj 122 */ 123 @AfterReturning(returning = "obj", pointcut = "cut()") 124 public void doAfterReturning(Object obj) { 125 // 处理完请求,返回相应参数 126 logger.info("Response Result:{}", JSONObject.toJSONString(obj)); 127 logger.info("------------------------ End ----------------------------" + LINE_SEPARATOR); 128 } 129 130 /** 131 * 文件上传字段不打印 132 * 133 * @param clazz 134 * @return 135 */ 136 private boolean isFile(Class clazz) { 137 if (MultipartFile.class.isAssignableFrom(clazz)) { 138 return true; 139 } 140 if ((clazz.isArray()) && (MultipartFile.class.isAssignableFrom(clazz.getComponentType()))) { 141 return true; 142 } 143 return false; 144 } 145 }
并发情况下打印参数导致线程串行问题,参考文章: