MyBatis插件开发与扩展:从原理到实战的完整指南

发布于:2025-08-15 ⋅ 阅读:(17) ⋅ 点赞:(0)

MyBatis插件开发与扩展:从原理到实战的完整指南

作为一名资深的Java开发者,我深知MyBatis在企业级应用中的重要地位。今天,我将带大家深入探索MyBatis的插件机制,从底层原理到实战应用,让你彻底掌握MyBatis的扩展能力!



🔌 MyBatis插件机制深度解析

插件原理与执行流程

MyBatis插件机制基于JDK动态代理和责任链模式,通过拦截器(Interceptor)对核心组件进行增强。让我们先看看插件的执行流程:

// MyBatis插件执行流程示意
public class MyBatisPluginFlow {
    // 1. 插件注册阶段
    public void registerPlugin() {
        // 在Configuration中注册插件
        configuration.addInterceptor(new MyCustomInterceptor());
    }
    
    // 2. 代理对象创建阶段
    public Object createProxy(Object target) {
        // MyBatis会为四大核心对象创建代理
        return Plugin.wrap(target, this);
    }
    
    // 3. 方法拦截执行阶段
    public Object intercept(Invocation invocation) {
        // 在这里执行自定义逻辑
        Object result = invocation.proceed();
        return result;
    }
}

插件执行时序图:

  1. 配置加载 → 解析插件配置
  2. 对象创建 → 为目标对象创建代理
  3. 方法调用 → 触发拦截器链
  4. 逻辑执行 → 执行自定义增强逻辑
  5. 结果返回 → 返回处理后的结果

Interceptor接口详解

Interceptor接口是MyBatis插件的核心,让我们深入了解其设计:

/**
 * MyBatis拦截器接口实现
 */
@Intercepts({
    @Signature(
        type = Executor.class,
        method = "update",
        args = {MappedStatement.class, Object.class}
    )
})
public class SqlExecutionTimeInterceptor implements Interceptor {
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        try {
            // 执行原方法
            Object result = invocation.proceed();
            return result;
        } finally {
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            
            // 记录SQL执行时间
            MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
            System.out.println("SQL [" + ms.getId() + "] 执行耗时: " + executionTime + "ms");
        }
    }
    
    @Override
    public Object plugin(Object target) {
        // 只对Executor进行代理
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }
    
    @Override
    public void setProperties(Properties properties) {
        // 设置插件属性
        String threshold = properties.getProperty("threshold", "1000");
        this.threshold = Long.parseLong(threshold);
    }
    
    private long threshold = 1000L;
}

关键注解说明:

  • @Intercepts:声明拦截器
  • @Signature:指定拦截的方法签名
  • type:拦截的对象类型
  • method:拦截的方法名
  • args:方法参数类型

四大核心对象拦截

MyBatis提供了四个核心拦截点,每个都有其特定的应用场景:

1. Executor拦截器
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class ExecutorInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 可以在这里实现:
        // - SQL执行监控
        // - 慢查询记录
        // - 数据权限控制
        // - 缓存策略
        
        String methodName = invocation.getMethod().getName();
        if ("query".equals(methodName)) {
            // 查询拦截逻辑
            return handleQuery(invocation);
        } else if ("update".equals(methodName)) {
            // 更新拦截逻辑
            return handleUpdate(invocation);
        }
        
        return invocation.proceed();
    }
    
    private Object handleQuery(Invocation invocation) throws Throwable {
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        
        // 添加数据权限过滤
        if (needDataPermissionFilter(ms.getId())) {
            parameter = addDataPermissionCondition(parameter);
            invocation.getArgs()[1] = parameter;
        }
        
        return invocation.proceed();
    }
    
    private Object handleUpdate(Invocation invocation) throws Throwable {
        // 自动填充创建时间、修改时间等
        Object parameter = invocation.getArgs()[1];
        autoFillAuditFields(parameter);
        
        return invocation.proceed();
    }
}
2. StatementHandler拦截器
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class StatementHandlerInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        
        // 获取SQL语句
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        
        // SQL重写示例:自动添加租户隔离条件
        if (needTenantIsolation(sql)) {
            String newSql = addTenantCondition(sql);
            // 通过反射修改SQL
            Field sqlField = boundSql.getClass().getDeclaredField("sql");
            sqlField.setAccessible(true);
            sqlField.set(boundSql, newSql);
        }
        
        return invocation.proceed();
    }
    
    private boolean needTenantIsolation(String sql) {
        // 判断是否需要租户隔离
        return sql.toLowerCase().contains("select") && 
               !sql.toLowerCase().contains("tenant_id");
    }
    
    private String addTenantCondition(String sql) {
        // 添加租户条件
        String tenantId = getCurrentTenantId();
        return sql + " AND tenant_id = '" + tenantId + "'";
    }
}
3. ParameterHandler拦截器
@Intercepts({
    @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})
})
public class ParameterHandlerInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
        
        // 获取参数对象
        Object parameterObject = parameterHandler.getParameterObject();
        
        // 参数加密处理
        if (parameterObject != null) {
            encryptSensitiveFields(parameterObject);
        }
        
        return invocation.proceed();
    }
    
    private void encryptSensitiveFields(Object parameter) {
        // 使用反射处理敏感字段加密
        Field[] fields = parameter.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Sensitive.class)) {
                field.setAccessible(true);
                try {
                    Object value = field.get(parameter);
                    if (value instanceof String) {
                        String encryptedValue = encrypt((String) value);
                        field.set(parameter, encryptedValue);
                    }
                } catch (IllegalAccessException e) {
                    // 处理异常
                }
            }
        }
    }
}
4. ResultSetHandler拦截器
@Intercepts({
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class ResultSetHandlerInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 执行原方法获取结果
        Object result = invocation.proceed();
        
        // 结果集后处理
        if (result instanceof List) {
            List<?> list = (List<?>) result;
            for (Object item : list) {
                // 敏感数据脱敏
                desensitizeData(item);
                // 数据权限过滤
                filterByDataPermission(item);
            }
        }
        
        return result;
    }
    
    private void desensitizeData(Object item) {
        // 实现数据脱敏逻辑
        Field[] fields = item.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Desensitize.class)) {
                field.setAccessible(true);
                try {
                    Object value = field.get(item);
                    if (value instanceof String) {
                        String maskedValue = maskSensitiveData((String) value);
                        field.set(item, maskedValue);
                    }
                } catch (IllegalAccessException e) {
                    // 处理异常
                }
            }
        }
    }
}

插件开发实战案例

让我们开发一个完整的SQL审计插件:

/**
 * SQL审计插件 - 记录所有SQL执行情况
 */
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
@Component
public class SqlAuditInterceptor implements Interceptor {
    
    private static final Logger logger = LoggerFactory.getLogger(SqlAuditInterceptor.class);
    
    @Autowired
    private SqlAuditService sqlAuditService;
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        String sqlId = null;
        String sql = null;
        Object parameter = null;
        
        try {
            // 获取SQL信息
            MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
            sqlId = ms.getId();
            parameter = invocation.getArgs()[1];
            
            // 获取实际执行的SQL
            BoundSql boundSql = ms.getBoundSql(parameter);
            sql = boundSql.getSql();
            
            // 执行原方法
            Object result = invocation.proceed();
            
            // 记录成功执行的SQL
            long executionTime = System.currentTimeMillis() - startTime;
            recordSqlExecution(sqlId, sql, parameter, executionTime, true, null, result);
            
            return result;
            
        } catch (Exception e) {
            // 记录执行失败的SQL
            long executionTime = System.currentTimeMillis() - startTime;
            recordSqlExecution(sqlId, sql, parameter, executionTime, false, e.getMessage(), null);
            throw e;
        }
    }
    
    private void recordSqlExecution(String sqlId, String sql, Object parameter, 
                                   long executionTime, boolean success, 
                                   String errorMessage, Object result) {
        try {
            SqlAuditRecord record = new SqlAuditRecord();
            record.setSqlId(sqlId);
            record.setSql(formatSql(sql));
            record.setParameter(JSON.toJSONString(parameter));
            record.setExecutionTime(executionTime);
            record.setSuccess(success);
            record.setErrorMessage(errorMessage);
            record.setExecuteTime(new Date());
            record.setUserId(getCurrentUserId());
            record.setUserName(getCurrentUserName());
            record.setIpAddress(getCurrentIpAddress());
            
            // 异步记录审计日志
            sqlAuditService.recordAsync(record);
            
            // 慢查询告警
            if (executionTime > getSlowQueryThreshold()) {
                alertSlowQuery(record);
            }
            
        } catch (Exception e) {
            logger.error("记录SQL审计日志失败", e);
        }
    }
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties) {
        // 设置慢查询阈值
        String threshold = properties.getProperty("slowQueryThreshold", "3000");
        this.slowQueryThreshold = Long.parseLong(threshold);
    }
    
    private long slowQueryThreshold = 3000L;
}

🛠️ 常用插件与工具实战

PageHelper分页插件

PageHelper是MyBatis生态中最受欢迎的分页插件:

<!-- Maven依赖 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.6</version>
</dependency>
# application.yml配置
pagehelper:
  helper-dialect: mysql
  reasonable: true
  support-methods-arguments: true
  params: count=countSql
  page-size-zero: true
/**
 * PageHelper使用示例
 */
@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 分页查询用户
     */
    public PageInfo<User> findUsersByPage(int pageNum, int pageSize, String keyword) {
        // 开启分页
        PageHelper.startPage(pageNum, pageSize);
        
        // 执行查询
        List<User> users = userMapper.findUsersByKeyword(keyword);
        
        // 封装分页信息
        return new PageInfo<>(users);
    }
    
    /**
     * 使用Lambda方式进行分页
     */
    public PageInfo<User> findUsersWithLambda(int pageNum, int pageSize) {
        return PageHelper.startPage(pageNum, pageSize)
                .doSelectPageInfo(() -> userMapper.selectAll());
    }
    
    /**
     * 自定义分页查询
     */
    public PageInfo<UserVO> findUserVOsByPage(UserQueryDTO queryDTO) {
        PageHelper.startPage(queryDTO.getPageNum(), queryDTO.getPageSize());
        
        // 复杂查询
        List<UserVO> userVOs = userMapper.findUserVOsWithConditions(queryDTO);
        
        PageInfo<UserVO> pageInfo = new PageInfo<>(userVOs);
        
        // 设置额外信息
        pageInfo.setTotal(pageInfo.getTotal());
        
        return pageInfo;
    }
}

PageHelper高级特性:

/**
 * PageHelper高级用法
 */
@RestController
public class UserController {
    
    /**
     * 支持排序的分页查询
     */
    @GetMapping("/users")
    public Result<PageInfo<User>> getUsers(
            @RequestParam(defaultValue = "1") int pageNum,
            @RequestParam(defaultValue = "10") int pageSize,
            @RequestParam(required = false) String orderBy) {
        
        // 设置排序
        if (StringUtils.isNotBlank(orderBy)) {
            PageHelper.startPage(pageNum, pageSize, orderBy);
        } else {
            PageHelper.startPage(pageNum, pageSize);
        }
        
        List<User> users = userService.findAllUsers();
        PageInfo<User> pageInfo = new PageInfo<>(users);
        
        return Result.success(pageInfo);
    }
    
    /**
     * 分页查询并统计
     */
    @GetMapping("/users/stats")
    public Result<UserPageStats> getUsersWithStats(
            @RequestParam(defaultValue = "1") int pageNum,
            @RequestParam(defaultValue = "10") int pageSize) {
        
        // 分页查询
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = userService.findAllUsers();
        PageInfo<User> pageInfo = new PageInfo<>(users);
        
        // 统计信息
        UserStats stats = userService.getUserStats();
        
        UserPageStats result = new UserPageStats();
        result.setPageInfo(pageInfo);
        result.setStats(stats);
        
        return Result.success(result);
    }
}

通用Mapper插件

通用Mapper提供了单表的CRUD操作:

<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>4.2.1</version>
</dependency>
/**
 * 通用Mapper使用示例
 */
@Table(name = "sys_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "username")
    private String username;
    
    @Column(name = "email")
    private String email;
    
    @Column(name = "create_time")
    private Date createTime;
    
    // getter/setter省略
}

/**
 * 继承通用Mapper接口
 */
public interface UserMapper extends Mapper<User>, MySqlMapper<User> {
    // 自动拥有基础CRUD方法
    // selectByPrimaryKey、selectAll、insert、updateByPrimaryKey等
    
    // 可以添加自定义方法
    List<User> findByUsername(String username);
}

/**
 * 服务层使用
 */
@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 条件查询示例
     */
    public List<User> findUsersByCondition(String username, String email) {
        Example example = new Example(User.class);
        Example.Criteria criteria = example.createCriteria();
        
        if (StringUtils.isNotBlank(username)) {
            criteria.andLike("username", "%" + username + "%");
        }
        
        if (StringUtils.isNotBlank(email)) {
            criteria.andEqualTo("email", email);
        }
        
        example.setOrderByClause("create_time desc");
        
        return userMapper.selectByExample(example);
    }
    
    /**
     * 批量操作示例
     */
    @Transactional
    public void batchInsertUsers(List<User> users) {
        for (User user : users) {
            user.setCreateTime(new Date());
            userMapper.insertSelective(user);
        }
    }
    
    /**
     * 动态更新示例
     */
    public int updateUserSelective(User user) {
        return userMapper.updateByPrimaryKeySelective(user);
    }
}

MyBatis-Plus增强工具

MyBatis-Plus是MyBatis的增强工具,提供了更强大的功能:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3</version>
</dependency>
/**
 * MyBatis-Plus实体类
 */
@TableName("sys_user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;
    
    private String email;
    
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
    @TableLogic
    private Integer deleted;
    
    @Version
    private Integer version;
    
    // getter/setter省略
}

/**
 * 继承BaseMapper
 */
public interface UserMapper extends BaseMapper<User> {
    // 自动拥有强大的CRUD功能
}

/**
 * 服务层继承ServiceImpl
 */
@Service
public class UserService extends ServiceImpl<UserMapper, User> {
    
    /**
     * 条件构造器查询
     */
    public List<User> findActiveUsers(String keyword) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(keyword), User::getUsername, keyword)
               .or()
               .like(StringUtils.isNotBlank(keyword), User::getEmail, keyword)
               .eq(User::getDeleted, 0)
               .orderByDesc(User::getCreateTime);
        
        return list(wrapper);
    }
    
    /**
     * 分页查询
     */
    public IPage<User> findUsersByPage(Page<User> page, String keyword) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(keyword), User::getUsername, keyword)
               .eq(User::getDeleted, 0);
        
        return page(page, wrapper);
    }
    
    /**
     * 批量更新
     */
    @Transactional
    public boolean batchUpdateStatus(List<Long> ids, Integer status) {
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.in(User::getId, ids)
               .set(User::getStatus, status);
        
        return update(wrapper);
    }
}

MyBatis-Plus配置类:

@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
    
    /**
     * 分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        
        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        
        // 防全表更新与删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        
        return interceptor;
    }
    
    /**
     * 自动填充处理器
     */
    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new MetaObjectHandler() {
            @Override
            public void insertFill(MetaObject metaObject) {
                this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
                this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
            }
            
            @Override
            public void updateFill(MetaObject metaObject) {
                this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
            }
        };
    }
}

代码生成器使用

MyBatis-Plus代码生成器:

/**
 * MyBatis-Plus代码生成器
 */
public class CodeGenerator {
    
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("your-name");
        gc.setOpen(false);
        gc.setServiceName("%sService");
        gc.setBaseResultMap(true);
        gc.setBaseColumnList(true);
        mpg.setGlobalConfig(gc);
        
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("password");
        mpg.setDataSource(dsc);
        
        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("system");
        pc.setParent("com.example");
        mpg.setPackageInfo(pc);
        
        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // 自定义属性注入
                Map<String, Object> map = new HashMap<>();
                map.put("date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                this.setMap(map);
            }
        };
        
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setInclude("sys_user", "sys_role"); // 需要生成的表名
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix("sys_"); // 表前缀
        mpg.setStrategy(strategy);
        
        // 模板引擎配置
        mpg.setTemplateEngine(new VelocityTemplateEngine());
        
        // 执行生成
        mpg.execute();
    }
}

自定义代码生成器模板:

/**
 * 自定义代码生成器
 */
@Component
public class CustomCodeGenerator {
    
    /**
     * 生成完整的CRUD代码
     */
    public void generateCrudCode(String tableName, String entityName) {
        // 生成Entity
        generateEntity(tableName, entityName);
        
        // 生成Mapper
        generateMapper(entityName);
        
        // 生成Service
        generateService(entityName);
        
        // 生成Controller
        generateController(entityName);
        
        // 生成前端页面
        generateVuePage(entityName);
    }
    
    private void generateEntity(String tableName, String entityName) {
        // 使用Freemarker模板生成Entity类
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("tableName", tableName);
        dataModel.put("entityName", entityName);
        dataModel.put("fields", getTableFields(tableName));
        
        generateFromTemplate("entity.ftl", dataModel, 
            "src/main/java/com/example/entity/" + entityName + ".java");
    }
    
    private void generateMapper(String entityName) {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("entityName", entityName);
        
        // 生成Mapper接口
        generateFromTemplate("mapper.ftl", dataModel,
            "src/main/java/com/example/mapper/" + entityName + "Mapper.java");
        
        // 生成Mapper XML
        generateFromTemplate("mapper-xml.ftl", dataModel,
            "src/main/resources/mapper/" + entityName + "Mapper.xml");
    }
    
    private void generateService(String entityName) {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("entityName", entityName);
        
        // 生成Service接口
        generateFromTemplate("service.ftl", dataModel,
            "src/main/java/com/example/service/" + entityName + "Service.java");
        
        // 生成Service实现
        generateFromTemplate("service-impl.ftl", dataModel,
            "src/main/java/com/example/service/impl/" + entityName + "ServiceImpl.java");
    }
    
    private void generateController(String entityName) {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("entityName", entityName);
        
        generateFromTemplate("controller.ftl", dataModel,
            "src/main/java/com/example/controller/" + entityName + "Controller.java");
    }
}

🚀 插件开发最佳实践

1. 性能优化建议

/**
 * 高性能插件开发要点
 */
@Intercepts({@Signature(type = Executor.class, method = "query", 
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class PerformanceOptimizedInterceptor implements Interceptor {
    
    // 使用ThreadLocal避免线程安全问题
    private static final ThreadLocal<Long> START_TIME = new ThreadLocal<>();
    
    // 使用缓存避免重复计算
    private final Map<String, Boolean> sqlIdCache = new ConcurrentHashMap<>();
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        String sqlId = ms.getId();
        
        // 快速判断是否需要处理
        if (!needIntercept(sqlId)) {
            return invocation.proceed();
        }
        
        START_TIME.set(System.currentTimeMillis());
        
        try {
            return invocation.proceed();
        } finally {
            long executionTime = System.currentTimeMillis() - START_TIME.get();
            START_TIME.remove(); // 防止内存泄漏
            
            // 异步处理,避免影响主流程
            processAsync(sqlId, executionTime);
        }
    }
    
    private boolean needIntercept(String sqlId) {
        return sqlIdCache.computeIfAbsent(sqlId, this::shouldIntercept);
    }
    
    private void processAsync(String sqlId, long executionTime) {
        CompletableFuture.runAsync(() -> {
            // 异步处理逻辑
            handleSlowQuery(sqlId, executionTime);
        });
    }
}

2. 插件链管理

/**
 * 插件链管理器
 */
@Configuration
public class InterceptorChainManager {
    
    @Bean
    @Order(1)
    public SqlAuditInterceptor sqlAuditInterceptor() {
        return new SqlAuditInterceptor();
    }
    
    @Bean
    @Order(2)
    public DataPermissionInterceptor dataPermissionInterceptor() {
        return new DataPermissionInterceptor();
    }
    
    @Bean
    @Order(3)
    public PerformanceMonitorInterceptor performanceMonitorInterceptor() {
        return new PerformanceMonitorInterceptor();
    }
    
    /**
     * 动态插件管理
     */
    @Bean
    public DynamicInterceptorManager dynamicInterceptorManager() {
        return new DynamicInterceptorManager();
    }
}

/**
 * 动态插件管理器
 */
public class DynamicInterceptorManager {
    
    private final List<Interceptor> interceptors = new CopyOnWriteArrayList<>();
    
    public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
    }
    
    public void removeInterceptor(Class<? extends Interceptor> interceptorClass) {
        interceptors.removeIf(interceptor -> 
            interceptor.getClass().equals(interceptorClass));
    }
    
    public void enableInterceptor(Class<? extends Interceptor> interceptorClass) {
        // 启用指定插件
    }
    
    public void disableInterceptor(Class<? extends Interceptor> interceptorClass) {
        // 禁用指定插件
    }
}

📊 总结与展望

MyBatis插件机制为我们提供了强大的扩展能力,通过合理使用插件,我们可以:

  1. 提升开发效率:通过代码生成器和通用Mapper减少重复代码
  2. 增强系统功能:实现数据权限、审计日志、性能监控等企业级功能
  3. 优化系统性能:通过缓存、分页、批量操作等提升系统性能
  4. 保证数据安全:通过加密、脱敏、权限控制保护敏感数据

希望这篇文章能帮助你深入理解MyBatis插件机制,在实际项目中灵活运用这些强大的扩展功能!


如果觉得这篇文章对你有帮助,欢迎点赞、收藏、转发!有任何问题也欢迎在评论区讨论交流! 🚀


网站公告

今日签到

点亮在社区的每一天
去签到