文中都是与mybatis-spring比较,与之相同之处,本文不再赘述。
对于启动流程不清楚的,可参考浅析MybatisAutoConfiguration流程
1. MybatisPlusAutoConfiguration
区别MybatisAutoConfiguration:
- 类注解部分引入的配置类换成了MybatisPlus定义的。
- afterPropertiesSet方法多了一步设置自定义配置的过程
- sqlSessionFactory内部变更,使用MybatisSqlSessionFactoryBean替代MybatisSessionFactoryBean
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {
@Override
public void afterPropertiesSet() {
// 这里可以实现自定义操作,例如设置主键生成策略
if (!CollectionUtils.isEmpty(mybatisPlusPropertiesCustomizers)){
mybatisPlusPropertiesCustomizers.forEach(i -> i.customize(properties));
}
checkConfigFileExists();
}
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
// 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
// 省略常规配置
.......
// 此处必为非 NULL
GlobalConfig globalConfig = this.properties.getGlobalConfig();
// 注入填充器
this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
// 注入主键生成器
this.getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i));
// 注入sql注入器
this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
// 注入ID生成器
this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);
// 设置 GlobalConfig 到 MybatisSqlSessionFactoryBean
factory.setGlobalConfig(globalConfig);
return factory.getObject();
}
}
2.MybatisMapperAnnotationBuilder
MybatisSqlSessionFactoryBean的buildSqlSessionFactory方法类似,这里就不重复说明了。
沿着MybatisSqlSessionFactoryBean调用顺序(如下图),直接看MybatisMapperAnnotationBuilder的parse方法。
对应于MapperAnnotationBuilder用于解析dao接口上的注解等信息,MybatisMapperAnnotationBuilder不仅实现上述功能,而且再这里注入了通用的crud操作。
public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
@Override
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
// 解析dao接口对应的xml文件,如果解析过则略过
loadXmlResource();
// 标识解析过资源了
configuration.addLoadedResource(resource);
final String typeName = type.getName();
assistant.setCurrentNamespace(typeName);
parseCache();
parseCacheRef();
SqlParserHelper.initSqlParserInfoCache(type);
Method[] methods = type.getMethods();
// 解析方法上的注解
for (Method method : methods) {
try {
if (!method.isBridge()) {
parseStatement(method);
SqlParserHelper.initSqlParserInfoCache(typeName, method);
}
} catch (IncompleteElementException e) {
// 解析失败的后面会再次尝试解析
configuration.addIncompleteMethod(new MybatisMethodResolver(this, method));
}
}
// 重点 MybatisPuls 自动注入crud
if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {
GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
}
}
parsePendingMethods();
}
}
接着看看AbstractSqlInjector类inspectInject方法
public abstract class AbstractSqlInjector implements ISqlInjector {
@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
Class<?> modelClass = extractModelClass(mapperClass);
if (modelClass != null) {
String className = mapperClass.toString();
Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
// 这里可以重写getMethodList,定制属于自己的通用方法
List<AbstractMethod> methodList = this.getMethodList(mapperClass);
if (CollectionUtils.isNotEmpty(methodList)) {
// 映射解析table信息
TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
// 循环注入自定义方法
methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
} else {
logger.debug(mapperClass.toString() + ", No effective injection method was found.");
}
mapperRegistryCache.add(className);
}
}
}
}
MybatisPlus启动过程中,做的初始化操作主要是上面这些地方。