MyBatis-Plus插件体系:自定义拦截器实现AOP式数据处理的完整教程
引言:为什么需要自定义拦截器?
在日常开发中,你是否遇到过这些痛点场景:
- 需要自动填充创建时间、更新时间等公共字段
- 要实现数据权限控制,根据用户角色动态过滤查询结果
- 需要对敏感数据进行加密存储和解密展示
- 要实现多租户数据隔离,自动添加租户ID条件
- 需要记录SQL执行日志进行性能监控
这些横切关注点(Cross-Cutting Concerns)如果散落在业务代码中,会导致代码重复、维护困难。MyBatis-Plus的插件体系提供了完美的AOP(Aspect-Oriented Programming)解决方案,让你能够以非侵入式的方式实现这些功能。
MyBatis-Plus插件架构解析
核心组件关系图
拦截器执行流程
自定义拦截器实战:数据自动填充拦截器
场景需求
我们需要在数据插入和更新时自动填充创建时间、更新时间、创建人、更新人等字段。
步骤1:定义实体类注解
import java.lang.annotation.*;
/**
* 自动填充注解
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AutoFill {
/**
* 填充类型
*/
FillType value();
enum FillType {
/**
* 插入时填充
*/
INSERT,
/**
* 更新时填充
*/
UPDATE,
/**
* 插入和更新时都填充
*/
INSERT_UPDATE
}
}
步骤2:实现自定义拦截器
package com.example.interceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Invocation;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
/**
* 自动填充拦截器
*/
public class AutoFillInterceptor implements InnerInterceptor {
private final FillValueProvider fillValueProvider;
public AutoFillInterceptor(FillValueProvider fillValueProvider) {
this.fillValueProvider = fillValueProvider;
}
@Override
public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) {
if (SqlCommandType.INSERT == ms.getSqlCommandType() ||
SqlCommandType.UPDATE == ms.getSqlCommandType()) {
processAutoFill(parameter, ms.getSqlCommandType());
}
}
private void processAutoFill(Object parameter, SqlCommandType commandType) {
if (parameter instanceof Map) {
Map<?, ?> paramMap = (Map<?, ?>) parameter;
Object entity = paramMap.get("et");
if (entity != null) {
fillEntityFields(entity, commandType);
}
} else if (parameter != null) {
fillEntityFields(parameter, commandType);
}
}
private void fillEntityFields(Object entity, SqlCommandType commandType) {
Class<?> entityClass = entity.getClass();
Field[] fields = entityClass.getDeclaredFields();
Arrays.stream(fields)
.filter(field -> field.isAnnotationPresent(AutoFill.class))
.forEach(field -> {
AutoFill autoFill = field.getAnnotation(AutoFill.class);
if (shouldFill(autoFill.value(), commandType)) {
fillField(entity, field, autoFill.value());
}
});
}
private boolean shouldFill(AutoFill.FillType fillType, SqlCommandType commandType) {
if (SqlCommandType.INSERT == commandType) {
return fillType == AutoFill.FillType.INSERT ||
fillType == AutoFill.FillType.INSERT_UPDATE;
} else if (SqlCommandType.UPDATE == commandType) {
return fillType == AutoFill.FillType.UPDATE ||
fillType == AutoFill.FillType.INSERT_UPDATE;
}
return false;
}
private void fillField(Object entity, Field field, AutoFill.FillType fillType) {
try {
field.setAccessible(true);
Object currentValue = field.get(entity);
// 如果字段已有值,则不填充
if (currentValue != null) {
return;
}
Object fillValue = getFillValue(field, fillType);
if (fillValue != null) {
field.set(entity, fillValue);
}
} catch (IllegalAccessException e) {
throw new RuntimeException("自动填充字段失败: " + field.getName(), e);
}
}
private Object getFillValue(Field field, AutoFill.FillType fillType) {
Class<?> fieldType = field.getType();
// 根据字段类型返回相应的填充值
if (LocalDateTime.class.equals(fieldType)) {
return LocalDateTime.now();
} else if (Date.class.equals(fieldType)) {
return new Date();
} else if (String.class.equals(fieldType)) {
return fillValueProvider.getCurrentUsername();
} else if (Long.class.equals(fieldType) || long.class.equals(fieldType)) {
return fillValueProvider.getCurrentUserId();
}
return null;
}
@Override
public void setProperties(Properties properties) {
// 可配置属性处理
}
}
/**
* 填充值提供者接口
*/
public interface FillValueProvider {
String getCurrentUsername();
Long getCurrentUserId();
}
步骤3:配置拦截器
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(FillValueProvider fillValueProvider) {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加自动填充拦截器
interceptor.addInnerInterceptor(new AutoFillInterceptor(fillValueProvider));
// 可以继续添加其他拦截器
// interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
// interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
@Bean
public FillValueProvider fillValueProvider() {
return new FillValueProvider() {
@Override
public String getCurrentUsername() {
// 从安全上下文获取当前用户名
return SecurityContextHolder.getContext().getAuthentication().getName();
}
@Override
public Long getCurrentUserId() {
// 从安全上下文获取当前用户ID
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return user.getId();
}
};
}
}
步骤4:使用示例
/**
* 用户实体类
*/
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
@AutoFill(AutoFill.FillType.INSERT)
private LocalDateTime createTime;
@AutoFill(AutoFill.FillType.INSERT_UPDATE)
private LocalDateTime updateTime;
@AutoFill(AutoFill.FillType.INSERT)
private String createBy;
@AutoFill(AutoFill.FillType.INSERT_UPDATE)
private String updateBy;
}
// 使用示例
User user = new User();
user.setUsername("testuser");
user.setPassword("password123");
userService.save(user); // 自动填充createTime、updateTime、createBy、updateBy字段
高级应用:数据权限拦截器
场景需求
根据用户角色动态添加数据权限过滤条件。
数据权限拦截器实现
package com.example.interceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Set;
/**
* 数据权限拦截器
*/
public class DataPermissionInterceptor implements InnerInterceptor {
private final DataPermissionProvider permissionProvider;
public DataPermissionInterceptor(DataPermissionProvider permissionProvider) {
this.permissionProvider = permissionProvider;
}
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 获取当前用户的数据权限规则
DataPermissionRule rule = permissionProvider.getCurrentPermissionRule();
if (rule == null || !rule.isEnabled()) {
return;
}
// 解析原始SQL
String originalSql = boundSql.getSql();
String modifiedSql = applyDataPermission(originalSql, rule);
// 使用反射修改BoundSql中的SQL
Field field = boundSql.getClass().getDeclaredField("sql");
field.setAccessible(true);
field.set(boundSql, modifiedSql);
}
private String applyDataPermission(String sql, DataPermissionRule rule) {
// 简单的SQL解析和条件添加
// 实际项目中可以使用JSqlParser等SQL解析库
String upperSql = sql.toUpperCase();
if (upperSql.contains(" WHERE ")) {
return sql.replace(" WHERE ", " WHERE " + rule.toSqlCondition() + " AND ");
} else if (upperSql.contains(" WHERE\n")) {
return sql.replace(" WHERE\n", " WHERE " + rule.toSqlCondition() + " AND\n");
} else {
// 找到FROM子句的位置
int fromIndex = upperSql.indexOf(" FROM ");
if (fromIndex != -1) {
// 在FROM后添加WHERE条件
int whereInsertIndex = fromIndex + 6; // " FROM "的长度
return sql.substring(0, whereInsertIndex) + " WHERE " +
rule.toSqlCondition() + sql.substring(whereInsertIndex);
}
}
return sql;
}
}
/**
* 数据权限规则
*/
public class DataPermissionRule {
private boolean enabled;
private String tableName;
private String columnName;
private Set<String> allowedValues;
public String toSqlCondition() {
if (!enabled || allowedValues.isEmpty()) {
return "1=1";
}
StringBuilder condition = new StringBuilder();
condition.append(columnName).append(" IN (");
int i = 0;
for (String value : allowedValues) {
if (i++ > 0) {
condition.append(",");
}
condition.append("'").append(value).append("'");
}
condition.append(")");
return condition.toString();
}
// getters and setters
}
性能优化与最佳实践
拦截器执行顺序管理
@Configuration
public class InterceptorOrderConfig {
@Bean
@Order(1)
public InnerInterceptor dataPermissionInterceptor() {
return new DataPermissionInterceptor();
}
@Bean
@Order(2)
public InnerInterceptor autoFillInterceptor() {
return new AutoFillInterceptor();
}
@Bean
@Order(3)
public InnerInterceptor paginationInterceptor() {
return new PaginationInnerInterceptor();
}
}
性能监控拦截器
public class PerformanceMonitorInterceptor implements InnerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitorInterceptor.class);
private static final ThreadLocal<Long> startTime = new ThreadLocal<>();
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
startTime.set(System.currentTimeMillis());
}
@Override
public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) {
startTime.set(System.currentTimeMillis());
}
@Override
public void afterQuery(Executor executor, MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, Object result) {
long cost = System.currentTimeMillis() - startTime.get();
if (cost > 1000) { // 超过1秒的记录警告日志
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



