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会执行以下关键步骤:
- 扫描继承BaseMapper的接口
- 注入通用CRUD方法对应的MappedStatement
- 初始化插件体系
- 注册元数据信息
五、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的核心组件实现功能增强,主要集成点包括:
- SqlSessionFactoryBean扩展 :
MybatisSqlSessionFactoryBean
继承并增强原生实现 - SQL注入器 :通过
ISqlInjector
接口注入通用CRUD方法 - 插件机制:利用MyBatis的Interceptor接口实现功能扩展
- 参数处理器 :扩展
ParameterHandler
处理特殊参数
九、性能优化机制
MP内置多种性能优化机制:
- SQL缓存:利用MyBatis一级、二级缓存减少重复查询
- 批量操作:JDBC批处理优化批量操作性能
- SQL优化:自动生成高效的SQL语句
- 惰性加载:按需加载关联对象
十、总结
MyBatis-Plus通过巧妙的设计,在不破坏MyBatis原有功能的基础上,提供了丰富的增强特性。其核心优势在于:
- 开发效率大幅提升:通用CRUD接口减少80%重复代码
- 功能丰富且易于扩展:插件体系支持各种自定义需求
- 性能优化内置:批量操作、SQL优化等机制提升执行效率
- 与Spring Boot无缝集成:自动配置简化使用流程
通过深入理解MyBatis-Plus的源码实现,开发者可以更好地利用其功能,并在需要时进行定制化扩展。