Mybatis-Plus(简称 MP)是一个 Mybatis 的加强工具, 那么它是怎么加强的呢?其实就是它曾经封装好了一些 crud 办法,开发就不须要再写 xml 了,间接调用这些办法就行,就相似于 JPA。那么这篇文章就来浏览以下 MP 的具体实现,看看是怎么实现这些加强的。
入口类:MybatisSqlSessionFactoryBuilder
通过在入口类 MybatisSqlSessionFactoryBuilder#build
办法中, 在利用启动时, 将 mybatis plus(简称 MP) 自定义的动静配置 xml 文件注入到 Mybatis 中。
public class MybatisSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {public SqlSessionFactory build(Configuration configuration) {
// ... 省略若干行
if (globalConfig.isEnableSqlRunner()) {new SqlRunnerInjector().inject(configuration);
}
// ... 省略若干行
return sqlSessionFactory;
}
}
这里波及到 2 个 MP2 个性能类
- 扩大继承自 Mybatis 的 MybatisConfiguration 类: MP 动静脚本构建,注册,及其它逻辑判断。
- SqlRunnerInjector: MP 默认插入一些动静办法的 xml 脚本办法。
MybatisConfiguration 类
这里咱们重点分析 MybatisConfiguration 类,在 MybatisConfiguration 中,MP 初始化了其本身的 MybatisMapperRegistry,而 MybatisMapperRegistry 是 MP 加载自定义的 SQL 办法的注册器。
MybatisConfiguration 中很多办法是应用 MybatisMapperRegistry 进行重写实现
其中有 3 个重载办法 addMapper 实现了注册 MP 动静脚本的性能。
public class MybatisConfiguration extends Configuration {
/**
* Mapper 注册
*/
protected final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);
// ....
/**
* 初始化调用
*/
public MybatisConfiguration() {super();
this.mapUnderscoreToCamelCase = true;
languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);
}
/**
* MybatisPlus 加载 SQL 程序:* <p> 1、加载 XML 中的 SQL </p>
* <p> 2、加载 SqlProvider 中的 SQL </p>
* <p> 3、XmlSql 与 SqlProvider 不能蕴含雷同的 SQL </p>
* <p> 调整后的 SQL 优先级:XmlSql > sqlProvider > CurdSql </p>
*/
@Override
public void addMappedStatement(MappedStatement ms) {// ...}
// ... 省略若干行
/**
* 应用本人的 MybatisMapperRegistry
*/
@Override
public <T> void addMapper(Class<T> type) {mybatisMapperRegistry.addMapper(type);
}
// .... 省略若干行
}
在 MybatisMapperRegistry 中,MP 将 mybatis 的 MapperAnnotationBuilder 替换为 MP 本人的 MybatisMapperAnnotationBuilder
public class MybatisMapperRegistry extends MapperRegistry {
@Override
public <T> void addMapper(Class<T> type) {
// ... 省略若干行
MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
parser.parse();
// ... 省略若干行
}
}
在 MybatisMapperRegistry 类的 addMapper 办法中,真正进入到 MP 的外围类 MybatisMapperAnnotationBuilder,MybatisMapperAnnotationBuilder 这个类是 MP 实现动静脚本的要害类。
MybatisMapperAnnotationBuilder 动静结构
在 MP 的外围类 MybatisMapperAnnotationBuilder 的 parser 办法中,MP 逐个遍历要加载的 Mapper 类,加载的办法包含上面几个
public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
@Override
public void parse() {
//... 省略若干行
for (Method method : type.getMethods()) {
/** for 循环代码, MP 判断 method 办法是否是 @Select @Insert 等 mybatis 注解办法 **/
parseStatement(method);
InterceptorIgnoreHelper.initSqlParserInfoCache(cache, mapperName, method);
SqlParserHelper.initSqlParserInfoCache(mapperName, method);
}
/** 这 2 行代码, MP 注入默认的办法列表 **/
if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
}
//... 省略若干行
}
@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {Class<?> modelClass = extractModelClass(mapperClass);
//... 省略若干行
List<AbstractMethod> methodList = this.getMethodList(mapperClass);
TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
// 循环注入自定义办法
methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
mapperRegistryCache.add(className);
}
}
public class DefaultSqlInjector extends AbstractSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
return Stream.of(new Insert(),
//... 省略若干行
new SelectPage()).collect(toList());
}
}
在 MybatisMapperAnnotationBuilder 中,MP 真正将框架自定义的动静 SQL 语句注册到 Mybatis 引擎中。而 AbstractMethod 则履行了具体方法的 SQL 语句结构。
具体的 AbstractMethod 实例类,结构具体的办法 SQL 语句
以 SelectById 这个类为例阐明下
/**
* 依据 ID 查问一条数据
*/
public class SelectById extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
/** 定义 mybatis xml method id, 对应 <id="xyz"> **/
SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
/** 结构 id 对应的具体 xml 片段 **/
SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),
sqlSelectColumns(tableInfo, false),
tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
tableInfo.getLogicDeleteSql(true, true)), Object.class);
/** 将 xml method 办法增加到 mybatis 的 MappedStatement 中 **/
return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
}
}
至此,MP 实现了在启动时加载自定义的办法 xml 配置的过程,前面的就是 mybatis ${变量}
#{变量}
的动静替换和预编译,曾经进入 mybatis 自有性能。
总结一下
MP 总共改写和替换了 mybatis 的十多个类,次要如下图所示:
总体上来说,MP 实现 mybatis 的加强,伎俩略显繁琐和不够直观,其实依据 MybatisMapperAnnotationBuilder 结构出自定义方法的 xml 文件,将其转换为 mybatis 的 Resource 资源,能够只继承重写一个 Mybatis 类:SqlSessionFactoryBean 比方如下:
public class YourSqlSessionFactoryBean extends SqlSessionFactoryBean implements ApplicationContextAware {private Resource[] mapperLocations;
@Override
public void setMapperLocations(Resource... mapperLocations) {super.setMapperLocations(mapperLocations);
/** 暂存应用 mybatis 原生定义的 mapper xml 文件门路 **/
this.mapperLocations = mapperLocations;
}
/**
* {@inheritDoc}
*/
@Override
public void afterPropertiesSet() throws Exception {ConfigurableListableBeanFactory beanFactory = getBeanFactory();
/** 只须要通过将自定义的办法结构成 xml resource 和原生定义的 Resource 一起注入到 mybatis 中即可, 这样就能够实现 MP 的自定义动静 SQL 和原生 SQL 的共生关系 **/
this.setMapperLocations(InjectMapper.getMapperResource(this.dbType, beanFactory, this.mapperLocations));
super.afterPropertiesSet();}
}
在这边文章中,简略介绍了 MP 实现动静语句的实现过程,并且给出一个可能的更便捷办法。