1. 需求
- 每当有请求访问服务A的接口的时候,服务A会把调用的方法,入参,返回的结果等相关信息存储下来,入库。
2. 实现原理
- 使用AOP方式,在不改变原来方法的基础上,对原来方法进行增强,使用的是环绕通知。
- 最终效果:自定义注解RequestLog,在方法上可以被使用,被该注解使用的方法,每次被调用的时候,就会记录调用的入参,返回值内容。
AOP相关知识请看:https://blog.csdn.net/xueyijin/article/details/121893953 - 代码:
-
先编写 记录请求日志的对象RequestLogEntity
@Builder @Data public class RequestLogEntity { // 请求来源 private RequestSource source; // 请求入参 private String request; // 请求返回结果 private String response; // 请求的方法名 private String apiName; // 调用方法的执行所需时间 private Long duration; // 创建时间 private Date techCreateTime; }
-
编写请求来源枚举
public enum RequestSource { SELECT, UPDATE, DELETE, INSERT, }
-
创建对应的数据库表
-
编写对应的dao层
public interface RequestLogDao { void insert(RequestLogEntity requestLogEntity); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.jin.king.RecordRequestLogUtils.api.RequestLogDao"> <insert id="insert" parameterType="requestLogEntity"> insert into requestLog values (#{apiName},#{source},#{request},#{response},#{duration},#{techCreateTime}) </insert> </mapper>
-
编写注解RequestLog
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface RequestLog { RequestSource source(); }
-
编写切面Aspect代码(核心代码)
@Component @Aspect @Slf4j @RequiredArgsConstructor public class RequestAspect { // 使用构造函数注入 // lombok的注解RequiredArgsConstructor // 要求 被构造的属性必须是 不可变类型 private final RequestLogDao requestLogDao; // 切点:被RequestLog注解注解的方法 @Pointcut("@annotation(requestLog)") private void AspectRequestLog(RequestLog requestLog){ } @Around("AspectRequestLog(requestLog)") public Object RecodeRequestLog(ProceedingJoinPoint joinPoint, RequestLog requestLog) throws Throwable{ // 获取被增强的方法名字 String apiName = joinPoint.getSignature().getName(); // 获取被增强方法的参数 Object[] args = joinPoint.getArgs(); // 将请求的参数转化为 字符串 String request = parseArgsToString(args); Object result; String response = null; Exception exception = null; long start = System.currentTimeMillis(); try{ result = joinPoint.proceed(args); } catch (Exception e){ log.error("请求报错:{0}", e.getMessage()); result = e.getMessage(); exception = e; } // 将请求的返回参数转化为 字符串 response = parseResultToString(result); long end = System.currentTimeMillis(); RequestLogEntity requestLogEntity = RequestLogEntity.builder() .apiName(apiName) .request(request) .response(response) .duration((end - start) / 1000L) .source(requestLog.source()) .techCreateTime(new Date()) .build(); // requestLogEntity 将对象写入到数据库中 记录下来 requestLogDao.insert(requestLogEntity); if(exception != null){ throw exception; } return result; } // 转化为String private String parseArgsToString(Object[] args){ return Arrays.stream(args).map(arg -> { if(arg instanceof String){ return (String) arg; } return JSONObject.toJSONString(arg); }).collect(Collectors.joining(",")); } private String parseResultToString(Object result){ return JSONObject.toJSONString(result); } }
-
3. 测试
-
编写了一个测试案例
@RestController @RequiredArgsConstructor public class UserController { private final UserService userService; @GetMapping("/select") // 加上RequestLog注解,调用这个方法的时候 // 会先去调用 RequestAspect 方法 @RequestLog(source = RequestSource.SELECT) public List<User> selectUser(){ return userService.selectUser(); } @PostMapping("/insert") @RequestLog(source = RequestSource.INSERT) public void insertUser(@RequestBody User user){ userService.insertUser(user); } }
@Service @RequiredArgsConstructor public class UserService { private final UserDao userDao; public List<User> selectUser() { return userDao.selectUser(); } public void insertUser(User user) { userDao.insertUser(user); } }
public interface UserDao { List<User> selectUser(); void insertUser(User user); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.jin.king.RecordRequestLogUtils.dao.UserDao"> <select id="selectUser" resultType="com.jin.king.RecordRequestLogUtils.domain.User"> select * from user; </select> <insert id="insertUser" parameterType="com.jin.king.RecordRequestLogUtils.domain.User"> insert into user values (#{username},#{password},#{hobby}) </insert> </mapper>
-
测试:
初始表 :分别是RequestLog表,以及User表
调用select接口:
调用insert接口: