本文介绍,mybatis-plus在项目启动的时候,解析实体类的注解,比如:@Table等,生成sqlNode,然后在真正访问的时候,再拼接成sql语句。
访问流程:
入口:
1. com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean#buildSqlSessionFactory
// 构建SqlSession的时候根据接口解析自动生成的sqlNode。
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
// 省略大部分代码
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
}
//2. 解析xml配置,也可以解析注解生成xml配置
org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
//这调用构建sqlsource
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
//3. 构建xml
org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// ignore, bound type is not required
}
if (boundType != null && !configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
//这里调用
configuration.addMapper(boundType);
}
}
}
//4. 解析mapper,并注册
com.baomidou.mybatisplus.core.MybatisConfiguration#addMapper
@Override
public <T> void addMapper(Class<T> type) {
mybatisMapperRegistry.addMapper(type);
}
// 5. 解析
com.baomidou.mybatisplus.core.MybatisMapperRegistry#addMapper
@Override
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
// TODO 如果之前注入 直接返回
return;
// TODO 这里就不抛异常了
// throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// TODO 这里也换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
knownMappers.put(type, new MybatisMapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
// TODO 这里也换成 MybatisMapperAnnotationBuilder 而不是 MapperAnnotationBuilder
//最重要的就是下面这2行
MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
//6. 解析注解的类
com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder#parse
@Override
public void parse() {
// 删除部分代码...
// TODO 注入 CURD 动态 SQL , 放在在最后, because 可能会有人会用注解重写sql
try {
// https://github.com/baomidou/mybatis-plus/issues/3038
if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {
parserInjector();
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new InjectorResolver(this));
}
}
//7. 解析注入
com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder#parserInjector
void parserInjector() {
GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
}
//8. 解析自定义方法
com.baomidou.mybatisplus.core.injector.AbstractSqlInjector#inspectInject
methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
//9.注入
com.baomidou.mybatisplus.core.injector.AbstractMethod#inject
public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
this.configuration = builderAssistant.getConfiguration();
this.builderAssistant = builderAssistant;
this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
/* 注入自定义方法 */
injectMappedStatement(mapperClass, modelClass, tableInfo);
}
大家,可以在上面这几个位置,打断点,然后再看。