文章目录
概要
在Spring Boot中结合MyBatis-Plus和AOP实现数据库操作日志记录,可通过以下步骤实现。该方案支持动态获取操作人ID、操作类型及时间,并通过异步处理提升性能:
一、环境准备
1.添加依赖
在pom.xml 中添加MyBatis-Plus和AOP依赖:
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>最新版本</version>
</dependency>
<!-- Spring AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.创建操作日志表
CREATE TABLE log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id VARCHAR(64) COMMENT '操作人ID',
operation_type VARCHAR(20) COMMENT '操作类型(INSERT/UPDATE/DELETE)',
method_name VARCHAR(200) COMMENT '执行方法',
params TEXT COMMENT '方法参数',
oper_time DATETIME COMMENT '操作时间'
);
二、核心实现步骤
1.定义操作日志实体与Mapper
日志实体类
@Data
@TableName("sys_oper_log")
public class OperLog {
@TableId(type = IdType.AUTO)
private Long id;
private String userId;
private String operationType;
private String methodName;
private String params;
private Date operTime;
}
Mapper
@Mapper
public interface OperLogMapper extends BaseMapper<OperLog> {}
2.自定义注解标记需记录的方法
在anno包创建动态标识监控增删改方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogOperation {
String type() default ""; // 操作类型:INSERT/UPDATE/DELETE
}
3.实现AOP切面
@Aspect
@Component
@Slf4j
public class OperLogAspect {
@Autowired
private OperLogMapper operLogMapper;
// 拦截所有标记@LogOperation的方法
@Pointcut("@annotation(com.example.annotation.LogOperation)")
public void logPointCut() {}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
LogOperation logAnnotation = method.getAnnotation(LogOperation.class);
// 执行原方法
Object result = joinPoint.proceed();
//异步记录日志可能会影响从线程中读取id
operLog.setUserId(UserUtils.getCurrentUserId()); // 从线程上下文获取用户ID
// 异步记录日志
CompletableFuture.runAsync(() -> {
OperLog operLog = new OperLog();
operLog.setOperationType(logAnnotation.type());
operLog.setMethodName(method.getName());
operLog.setParams(JSON.toJSONString(joinPoint.getArgs()));
operLog.setOperTime(new Date());
operLogMapper.insert(operLog);
});
return result;
}
}
调用MyBatis-Plus中的Mapper方法要成功监控需要在@Pointcut注解中标计
额外添加
@Pointcut(
"@annotation(com.pet.user.anno.Log)||"+
"execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.insert(..)) || " +
"execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.delete*(..)) || " +
"execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.update*(..))")
将日志插入数据库的操作需要排除
插入方法最好自己写,然后排除掉,不排除容易死循环
@Pointcut("!execution(* com.pet.user.mapper.LogMapper.*(..))&& "+//排除插入数据库的方法
"@annotation(com.pet.user.anno.Log)||"+
"execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.insert(..)) || " +
"execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.delete*(..)) || " +
"execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.update*(..))")
4.在MyBatis-Plus的Service层应用注解(或者Mapper层)
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
@LogOperation(type = "INSERT")
public boolean save(User user) {
return super.save(user);
}
@Override
@LogOperation(type = "UPDATE")
public boolean updateById(User user) {
return super.updateById(user);
}
}