springboot工程中使用AOP记录接口访问日志

mac2025-06-05  16

项目源码 本文章主要记录controller层的访问记录

AOP

通知(Advice) 前置通知(Before):在目标方法调用前调用通知功能; 后置通知(After):在目标方法调用之后调用通知功能,不关心方法的返回结果; 返回通知(AfterReturning):在目标方法成功执行之后调用通知功能; 异常通知(AfterThrowing):在目标方法抛出异常后调用通知功能; 环绕通知(Around):通知包裹了目标方法,在目标方法调用之前和之后执行自 定义的行为。

连接点(JoinPoint) 通知功能被应用的时机。比如接口方法被调用的时候就是日志切面的连接点。

切点(Pointcut) 切点定义了通知功能被应用的范围。比如日志切面的应用范围就是所有接口,即 所有controller层的接口方法。

切面(Aspect) 切面是通知和切点的结合,定义了何时、何地应用通知功能。

引入(Introduction) 在无需修改现有类的情况下,向现有的类添加新方法或属性。

织入(Weaving) 把切面应用到目标对象并创建新的代理对象的过程。

常用注解

@Aspect:用于定义切面@Before:通知方法会在目标方法调用之前执行@After:通知方法会在目标方法返回或抛出异常后执行@AfterReturning:通知方法会在目标方法返回后执行@AfterThrowing:通知方法会在目标方法抛出异常后执行@Around:通知方法会将目标方法封装起来@Pointcut:定义切点表达式

切点表达式

execution(方法修饰符 返回类型 方法所属的包.类名.方法名称(方法参数)

定义日志类

@Data public class WebLog { /** * 操作描述 */ private String description; /** * 操作用户 */ private String username; /** * 操作时间 */ private Long startTime; /** * 消耗时间 */ private Integer spendTime; /** * 根路径 */ private String basePath; /** * URI */ private String uri; /** * URL */ private String url; /** * 请求类型 */ private String method; /** * IP地址 */ private String ip; /** * 请求参数 */ private Object parameter; /** * 请求返回的结果 */ private Object result; }

定义切面类

@Component @Aspect // 定义切面类 @Order(1) // 切面类的执行顺序 public class WebLogAspect { private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class); // 切入点 (切入点表达式记得跟换为自己的) @Pointcut("execution(public * com.keyou.controller.*.*(..))") public void webLog() { } // 前置通知 设置切入点表达式 @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { } // 后置通知 @AfterReturning(value = "webLog()", returning = "ret") public void doAfterReturning(Object ret) throws Throwable { } //ProceedingJoinPoint joinPoint 形参不可以改变 @Around("webLog()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { //获取开始时间 long startTime = System.currentTimeMillis(); //获取请求对象 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //记录请求消息 WebLog webLog = new WebLog(); //获取操作描述(controller层都有@ApiOperation注解,获取注解内容) Object result = joinPoint.proceed(); Signature signature = joinPoint.getSignature();// 获取签名 MethodSignature methodSignature = (MethodSignature) signature;// 执行的是方法 装换为method签名 Method method = methodSignature.getMethod();// 获取方法 if (method.isAnnotationPresent(ApiOperation.class)) {//获取方法上注解 ApiOperation annotation = method.getAnnotation(ApiOperation.class); webLog.setDescription(annotation.value()); //设置操作描述 } // 结束时间 long endTime = System.currentTimeMillis(); String urlStr = request.getRequestURL().toString(); webLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath())); // 设置根路径 webLog.setUsername(request.getRemoteUser()); // 设置操作用户 webLog.setIp(request.getRemoteAddr()); webLog.setMethod(request.getMethod()); // 请求类型 webLog.setParameter(joinPoint.getArgs()); // 请求参数 webLog.setResult(result); // 请求结果 webLog.setSpendTime((int) (endTime - startTime)); webLog.setStartTime(startTime); webLog.setUri(request.getRequestURI()); webLog.setUrl(request.getRequestURL().toString()); LOGGER.info("{}", JSONUtil.parse(webLog)); return result; } /** * 根据方法和传入的参数获取请求参数 */ private Object getParameter(Method method, Object[] args) { List<Object> argList = new ArrayList<>(); Parameter[] parameters = method.getParameters(); for (int i = 0; i < parameters.length; i++) { //将RequestBody注解修饰的参数作为请求参数 RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class); if (requestBody != null) { argList.add(args[i]); } //将RequestParam注解修饰的参数作为请求参数 RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class); if (requestParam != null) { Map<String, Object> map = new HashMap<>(); String key = parameters[i].getName(); if (!StringUtils.isEmpty(requestParam.value())) { key = requestParam.value(); } map.put(key, args[i]); argList.add(map); } } if (argList.size() == 0) { return null; } else if (argList.size() == 1) { return argList.get(0); } else { return argList; } } }
最新回复(0)