MyBatis Plus 源码深度解析

MyBatis Plus 源码深度解析

一、核心架构设计与设计理念

MyBatis-Plus(简称MP)是MyBatis的增强工具,以无侵入性为核心设计理念,在MyBatis基础上仅做功能扩展而不修改原生逻辑。通过内置通用CRUD、代码生成器等模块,MP将单表操作效率提升至极致。

MP的整体架构主要包含以下几层:

  • 接口层:BaseMapper、IService等通用接口
  • 核心处理层:SQL注入器、条件构造器、插件体系
  • 基础支撑层:与MyBatis集成的适配组件

二、核心组件源码实现

1. BaseMapper动态代理机制

BaseMapper是MP提供的核心接口,通过动态代理机制实现SQL自动生成。源码核心在于MapperProxy类的实现:

java 复制代码
// MapperProxy核心实现逻辑
public class MapperProxy<T> implements InvocationHandler, Serializable {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 处理Object类方法
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        // 处理接口默认方法
        if (method.isDefault()) {
            return invokeDefaultMethod(proxy, method, args);
        }
        // 创建MapperMethod并执行
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }
    
    // 缓存MapperMethod对象
    private MapperMethod cachedMapperMethod(Method method) {
        return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
    }
}

MP通过MybatisMapperAnnotationBuilder在初始化时,将BaseMapper的通用方法注入到MyBatis的Configuration中:

java 复制代码
// SQL注入核心实现
public class DefaultSqlInjector extends AbstractSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        return Stream.of(
            new Insert(),
            new Delete(),
            new Update(),
            new SelectById(),
            new SelectBatchByIds(),
            new SelectByMap(),
            new SelectOne(),
            new SelectCount(),
            new SelectList()
            // 更多方法...
        ).collect(toList());
    }
}

2. Wrapper条件构造器实现

Wrapper是MP提供的强大条件构造API,其核心实现在AbstractWrapper类中:

java 复制代码
// AbstractWrapper核心方法
protected <V> Children doIt(String column, V val, SqlKeyword sqlKeyword, String... append) {
    return columnToString(column, val, sqlKeyword, append);
}

protected <V> Children columnToString(String column, V val, SqlKeyword sqlKeyword, String... append) {
    return addCondition(columnToString(column, sqlKeyword, val), val);
}

LambdaWrapper通过Lambda表达式获取属性名,避免硬编码:

java 复制代码
// Lambda表达式处理
public static <T> String getColumn(LambdaExpression<T, ?> lambdaExpression) {
    // 解析Lambda表达式获取属性名
    SerializedLambda serializedLambda = getSerializedLambda(lambdaExpression);
    String implMethodName = serializedLambda.getImplMethodName();
    // 从方法名提取字段名,如getUserName -> user_name
    String fieldName = StringUtils.resolveFieldName(implMethodName);
    return fieldName;
}

三、插件体系与扩展机制

1. 分页插件源码解析

分页插件是MP最常用的插件之一,核心实现在PaginationInnerInterceptor类:

java 复制代码
// 分页插件核心逻辑
@Override
public Object intercept(Invocation invocation) throws Throwable {
    // 获取原始SQL
    StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
    MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
    // 解析分页参数
    processPage(metaObject, invocation);
    // 执行原始SQL
    return invocation.proceed();
}

// 处理分页逻辑
protected void processPage(MetaObject metaObject, Invocation invocation) {
    // 获取Page对象
    Page<?> page = getPage(metaObject);
    if (page == null) {
        return;
    }
    // 重写SQL为分页查询
    String originalSql = (String) metaObject.getValue(BOUND_SQL_SQL);
    String pageSql = dialect.buildPaginationSql(originalSql, page.offset(), page.getSize());
    metaObject.setValue(BOUND_SQL_SQL, pageSql);
    // 处理总数查询
    countTotal(invocation, metaObject, page, originalSql);
}

2. 乐观锁插件实现

OptimisticLockerInnerInterceptor实现乐观锁机制,通过版本号控制并发:

java 复制代码
// 乐观锁核心逻辑
@Override
protected void processUpdate(InnerInterceptor.InnerInterceptorChain chain, MetaObject metaObject, String sql, Object parameterObject, Object target, int index) {
    // 检查是否需要处理乐观锁
    if (!(parameterObject instanceof MapperMethod.ParamMap)) {
        return;
    }
    // 获取版本号字段信息
    TableInfo tableInfo = TableInfoHelper.getTableInfo(target.getClass());
    if (tableInfo == null || tableInfo.getVersionFieldInfo() == null) {
        return;
    }
    // 处理版本号递增和条件设置
    FieldInfo versionField = tableInfo.getVersionFieldInfo();
    Object originalVersion = versionField.get(target);
    // 版本号加1
    Object newVersion = getNewVersion(originalVersion, versionField);
    // 设置新的版本号
    versionField.set(target, newVersion);
    // 添加版本号条件
    ((MapperMethod.ParamMap<?>) parameterObject).put(VERSION_ORIGINAL, originalVersion);
}

3. 多租户插件实现

TenantLineInnerInterceptor实现多租户数据隔离:

java 复制代码
// 多租户插件核心逻辑
@Override
protected void processSelect(InnerInterceptor.InnerInterceptorChain chain, MetaObject metaObject, String sql, Object parameterObject, BoundSql boundSql) {
    // 获取租户处理器
    TenantLineHandler tenantLineHandler = getTenantLineHandler();
    if (!tenantLineHandler.ignoreTable(getTableName(sql))) {
        // 重写SQL,添加租户ID条件
        String newSql = tenantLineHandler.buildSql(sql, getTableName(sql));
        metaObject.setValue(BOUND_SQL_SQL, newSql);
    }
}

四、自动配置与启动流程

MP与Spring Boot集成的核心在于MybatisPlusAutoConfiguration类:

java 复制代码
// MP自动配置类
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisPlusProperties.class)
public class MybatisPlusAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        // 使用MyBatis-Plus的SqlSessionFactoryBean
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        
        // 配置MyBatis-Plus特定组件
        factory.setGlobalConfig(globalConfig);
        factory.setPlugins(plugins);
        factory.setTypeHandlers(typeHandlers);
        
        // 注入SQL注入器
        if (globalConfig.getSqlInjector() != null) {
            factory.setSqlInjector(globalConfig.getSqlInjector());
        }
        
        return factory.getObject();
    }
}

在启动过程中,MP会执行以下关键步骤:

  1. 扫描继承BaseMapper的接口
  2. 注入通用CRUD方法对应的MappedStatement
  3. 初始化插件体系
  4. 注册元数据信息

五、Service层批量操作优化

MP的IService接口提供了高效的批量操作方法,核心实现在ServiceImpl类:

java 复制代码
// 批量保存实现
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveBatch(Collection<T> entityList, int batchSize) {
    String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE);
    return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
}

// 批量执行核心方法
protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
    return SqlHelper.executeBatch(this.entityClass, this.log, list, batchSize, consumer);
}

六、代码生成器原理

MP的代码生成器通过模板引擎和数据库元数据读取实现快速代码生成:

java 复制代码
// 代码生成器核心逻辑
public AutoGenerator execute() {
    // 初始化配置
    initEntityNameConvert();
    
    // 生成各层代码
    if (null != config.getStrategyConfig().getSuperEntityClass()) {
        generateByTable(config);
    } else {
        generateByPackage(config);
    }
    
    return this;
}

// 按表生成代码
protected void generateByTable(ConfigBuilder config) {
    // 读取表元数据
    List<TableInfo> tableList = config.getTableInfoList();
    for (TableInfo tableInfo : tableList) {
        // 生成实体类
        if (null != tableInfo && StringUtils.isNotBlank(tableInfo.getEntityName())) {
            generateEntity(tableInfo);
            generateMapper(tableInfo);
            generateXml(tableInfo);
            generateService(tableInfo);
            generateController(tableInfo);
        }
    }
}

七、高级特性实现

1. 自动填充功能

MetaObjectHandler实现字段自动填充:

java 复制代码
// 自动填充核心方法
@Override
public void insertFill(MetaObject metaObject) {
    // 设置创建时间
    this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
    // 设置更新时间
    this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}

@Override
public void updateFill(MetaObject metaObject) {
    // 设置更新时间
    this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}

2. 动态表名替换

通过TableNameHandler实现动态表名:

java 复制代码
// 动态表名处理器示例
public class DynamicTableNameHandler implements TableNameHandler {
    @Override
    public String dynamicTableName(String sql, String tableName) {
        // 从ThreadLocal获取表名后缀
        String suffix = TableNameContextHolder.getSuffix();
        if (StringUtils.isNotBlank(suffix)) {
            return tableName + "_" + suffix;
        }
        return tableName;
    }
}

八、与MyBatis的集成机制

MP通过扩展MyBatis的核心组件实现功能增强,主要集成点包括:

  1. SqlSessionFactoryBean扩展MybatisSqlSessionFactoryBean继承并增强原生实现
  2. SQL注入器 :通过ISqlInjector接口注入通用CRUD方法
  3. 插件机制:利用MyBatis的Interceptor接口实现功能扩展
  4. 参数处理器 :扩展ParameterHandler处理特殊参数

九、性能优化机制

MP内置多种性能优化机制:

  1. SQL缓存:利用MyBatis一级、二级缓存减少重复查询
  2. 批量操作:JDBC批处理优化批量操作性能
  3. SQL优化:自动生成高效的SQL语句
  4. 惰性加载:按需加载关联对象

十、总结

MyBatis-Plus通过巧妙的设计,在不破坏MyBatis原有功能的基础上,提供了丰富的增强特性。其核心优势在于:

  1. 开发效率大幅提升:通用CRUD接口减少80%重复代码
  2. 功能丰富且易于扩展:插件体系支持各种自定义需求
  3. 性能优化内置:批量操作、SQL优化等机制提升执行效率
  4. 与Spring Boot无缝集成:自动配置简化使用流程

通过深入理解MyBatis-Plus的源码实现,开发者可以更好地利用其功能,并在需要时进行定制化扩展。

相关推荐
货拉拉技术2 小时前
大规模 Kafka 消费集群调度方案
后端
oak隔壁找我2 小时前
Druid 数据库连接池源码详细解析
java·数据库·后端
剽悍一小兔2 小时前
Nginx 基本使用配置大全
后端
LCG元2 小时前
性能排查必看!当Linux服务器CPU/内存飙高,如何快速定位并"干掉"罪魁祸首进程?
linux·后端
oak隔壁找我2 小时前
MyBatis 源码深度解析
java·后端
lang201509282 小时前
Spring 4.1新特性:深度优化与生态整合
java·后端·spring
纳就这样吧2 小时前
Spring Cloud中@EnableDiscoveryClient注解详解
spring boot·后端·spring cloud
李慕婉学姐2 小时前
【开题答辩过程】以《重庆市社区养老服务小程序设计与实现》为例,不会开题答辩的可以进来看看
java·spring boot
DBLens数据库管理和开发工具2 小时前
GROUP BY隐性排序:MySQL 5.x 与 8.x 的行为大不同
后端