我是一段不羁的公告!
记得给艿艿这 3 个项目加油,添加一个 STAR 噢。
https://github.com/YunaiV/SpringBoot-Labs
https://github.com/YunaiV/onemall
https://github.com/YunaiV/ruoyi-vue-pro

精尽 MyBatis 源码分析 —— MyBatis 初始化(四)之加载注解配置

1. 概述

本文接 《精尽 MyBatis 源码分析 —— MyBatis 初始化(三)之加载 Statement 配置》 一文,来分享 MyBatis 初始化的第 2 步的一部分,加载注解配置。而这个部分的入口是 MapperAnnotationBuilder 。下面,我们一起来看看它的代码实现。

《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》「3.3.13 mapperElement」 中,我们已经看到对 MapperAnnotationBuilder 的调用代码。代码如下:

// Configuration.java

public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}

// MapperRegistry.java

public <T> void addMapper(Class<T> type) {
// 判断,必须是接口。
if (type.isInterface()) {
// 已经添加过,则抛出 BindingException 异常
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 添加到 knownMappers 中
knownMappers.put(type, new MapperProxyFactory<>(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.
// 解析 Mapper 的注解配置 <====== 😈 看我 😈 =====>
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
// 标记加载完成
loadCompleted = true;
} finally {
// 若加载未完成,从 knownMappers 中移除
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
  • 调用处,请看 😈 处。

2. MapperAnnotationBuilder

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder ,Mapper 注解构造器,负责解析 Mapper 接口上的注解。

2.1 构造方法

// MapperAnnotationBuilder.java

/**
* SQL 操作注解集合
*/
private static final Set<Class<? extends Annotation>> SQL_ANNOTATION_TYPES = new HashSet<>();
/**
* SQL 操作提供者注解集合
*/
private static final Set<Class<? extends Annotation>> SQL_PROVIDER_ANNOTATION_TYPES = new HashSet<>();

private final Configuration configuration;
private final MapperBuilderAssistant assistant;
/**
* Mapper 接口类
*/
private final Class<?> type;

static {
SQL_ANNOTATION_TYPES.add(Select.class);
SQL_ANNOTATION_TYPES.add(Insert.class);
SQL_ANNOTATION_TYPES.add(Update.class);
SQL_ANNOTATION_TYPES.add(Delete.class);

SQL_PROVIDER_ANNOTATION_TYPES.add(SelectProvider.class);
SQL_PROVIDER_ANNOTATION_TYPES.add(InsertProvider.class);
SQL_PROVIDER_ANNOTATION_TYPES.add(UpdateProvider.class);
SQL_PROVIDER_ANNOTATION_TYPES.add(DeleteProvider.class);
}

public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
// 创建 MapperBuilderAssistant 对象
String resource = type.getName().replace('.', '/') + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;
}
  • 比较简单,胖友扫一眼。

2.2 parse

#parse() 方法,解析注解。代码如下:

// MapperAnnotationBuilder.java

public void parse() {
// <1> 判断当前 Mapper 接口是否应加载过。
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
// <2> 加载对应的 XML Mapper
loadXmlResource();
// <3> 标记该 Mapper 接口已经加载过
configuration.addLoadedResource(resource);
// <4> 设置 namespace 属性
assistant.setCurrentNamespace(type.getName());
// <5> 解析 @CacheNamespace 注解
parseCache();
// <6> 解析 @CacheNamespaceRef 注解
parseCacheRef();
// <7> 遍历每个方法,解析其上的注解
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
// <7.1> 执行解析
parseStatement(method);
}
} catch (IncompleteElementException e) {
// <7.2> 解析失败,添加到 configuration 中
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
// <8> 解析待定的方法
parsePendingMethods();
}
  • <1> 处,调用 Configuration#isResourceLoaded(String resource) 方法,判断当前 Mapper 接口是否应加载过。
  • <2> 处,调用 #loadXmlResource() 方法,加载对应的 XML Mapper 。详细解析,见 「2.3 loadXmlResource」
  • <3> 处,调用 Configuration#addLoadedResource(String resource) 方法,标记该 Mapper 接口已经加载过。
  • <4> 处,调用 MapperBuilderAssistant#setCurrentNamespace(String currentNamespace) 方法,设置 namespace 属性。
  • <5> 处,调用 #parseCache() 方法,@CacheNamespace 注解。详细解析,见 「2.4 parseCache」
  • <6> 处,调用 #parseCacheRef() 方法,@CacheNamespaceRef 注解。详细解析,见 「2.5 parseCacheRef」
  • <7> 处,遍历每个方法,解析其上的注解。

    • <7.1> 处,调用 #parseStatement(Method method) 方法,执行解析每个方法的注解。详细解析,见 「2.6 parseStatement」
    • <7.2> 处,解析失败,调用 Configuration#addIncompleteMethod(MethodResolver builder) 方法,添加到 configuration 中。代码如下:

      // Configuration.java

      /**
      * 未完成的 MethodResolver 集合
      */
      protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();

      public void addIncompleteMethod(MethodResolver builder) {
      incompleteMethods.add(builder);
      }
  • <8> 处,调用 #parsePendingMethods() 方法,解析待定的方法。详细解析,见 「2.8 parsePendingMethods」

2.3 loadXmlResource

#loadXmlResource() 方法,加载对应的 XML Mapper 。代码如下:

// MapperAnnotationBuilder.java

private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
// <1> 判断 Mapper XML 是否已经加载过,如果加载过,就不加载了。
// 此处,是为了避免和 XMLMapperBuilder#parse() 方法冲突,重复解析
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
// <2> 获得 InputStream 对象
String xmlResource = type.getName().replace('.', '/') + ".xml";
// #1347
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
if (inputStream == null) {
// Search XML mapper that is not in the module but in the classpath.
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e2) {
// ignore, resource is not required
}
}
// <2> 创建 XMLMapperBuilder 对象,执行解析
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
}
}
}
  • <1> 处,判断 Mapper XML 是否已经加载过,如果加载过,就不加载了。此处,是为了避免和 XMLMapperBuilder#parse() 方法冲突,重复解析。
  • <2> 处,获得 InputStream 对象,然后创建 XMLMapperBuilder 对象,最后调用 XMLMapperBuilder#parse() 方法,执行解析。
  • 这里,如果是先解析 Mapper 接口,那就会达到再解析对应的 Mapper XML 的情况。

2.4 parseCache

#parseCache() 方法,解析 @CacheNamespace 注解。代码如下:

// MapperAnnotationBuilder.java

private void parseCache() {
// <1> 获得类上的 @CacheNamespace 注解
CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
if (cacheDomain != null) {
// <2> 获得各种属性
Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
// <3> 获得 Properties 属性
Properties props = convertToProperties(cacheDomain.properties());
// <4> 创建 Cache 对象
assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props);
}
}
  • <1> 处,获得上的 @CacheNamespace 注解。
  • <2> 处,获得各种属性。
  • <3> 处,调用 #convertToProperties(Property[] properties) 方法,将 @Property 注解数组,转换成 Properties 对象。代码如下:

    // MapperAnnotationBuilder.java

    private Properties convertToProperties(Property[] properties) {
    if (properties.length == 0) {
    return null;
    }
    Properties props = new Properties();
    for (Property property : properties) {
    props.setProperty(property.name(),
    PropertyParser.parse(property.value(), configuration.getVariables())); // 替换
    }
    return props;
    }

2.5 parseCacheRef

#parseCacheRef() 方法,解析 @CacheNamespaceRef 注解。代码如下:

// MapperAnnotationBuilder.java

private void parseCacheRef() {
// 获得类上的 @CacheNamespaceRef 注解
CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
if (cacheDomainRef != null) {
// <2> 获得各种属性
Class<?> refType = cacheDomainRef.value();
String refName = cacheDomainRef.name();
// <2> 校验,如果 refType 和 refName 都为空,则抛出 BuilderException 异常
if (refType == void.class && refName.isEmpty()) {
throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef");
}
// <2> 校验,如果 refType 和 refName 都不为空,则抛出 BuilderException 异常
if (refType != void.class && !refName.isEmpty()) {
throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef");
}
// <2> 获得最终的 namespace 属性
String namespace = (refType != void.class) ? refType.getName() : refName;
// <3> 获得指向的 Cache 对象
assistant.useCacheRef(namespace);
}
}

2.6 parseStatement

#parseStatement(Method method) 方法,解析方法上的 SQL 操作相关的注解。代码如下:

// MapperAnnotationBuilder.java

void parseStatement(Method method) {
// <1> 获得参数的类型
Class<?> parameterTypeClass = getParameterType(method);
// <2> 获得 LanguageDriver 对象
LanguageDriver languageDriver = getLanguageDriver(method);
// <3> 获得 SqlSource 对象
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
if (sqlSource != null) {
// <4> 获得各种属性
Options options = method.getAnnotation(Options.class);
final String mappedStatementId = type.getName() + "." + method.getName();
Integer fetchSize = null;
Integer timeout = null;
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = null;
SqlCommandType sqlCommandType = getSqlCommandType(method);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = !isSelect;
boolean useCache = isSelect;

// <5> 获得 KeyGenerator 对象
KeyGenerator keyGenerator;
String keyProperty = null;
String keyColumn = null;
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) { // 有
// first check for SelectKey annotation - that overrides everything else
// <5.1> 如果有 @SelectKey 注解,则进行处理
SelectKey selectKey = method.getAnnotation(SelectKey.class);
if (selectKey != null) {
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
keyProperty = selectKey.keyProperty();
// <5.2> 如果无 @Options 注解,则根据全局配置处理
} else if (options == null) {
keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
// <5.3> 如果有 @Options 注解,则使用该注解的配置处理
} else {
keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
keyProperty = options.keyProperty();
keyColumn = options.keyColumn();
}
// <5.4> 无
} else {
keyGenerator = NoKeyGenerator.INSTANCE;
}

// <6> 初始化各种属性
if (options != null) {
if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
flushCache = true;
} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
flushCache = false;
}
useCache = options.useCache();
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
timeout = options.timeout() > -1 ? options.timeout() : null;
statementType = options.statementType();
resultSetType = options.resultSetType();
}

// <7> 获得 resultMapId 编号字符串
String resultMapId = null;
// <7.1> 如果有 @ResultMap 注解,使用该注解为 resultMapId 属性
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
if (resultMapAnnotation != null) {
String[] resultMaps = resultMapAnnotation.value();
StringBuilder sb = new StringBuilder();
for (String resultMap : resultMaps) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(resultMap);
}
resultMapId = sb.toString();
// <7.2> 如果无 @ResultMap 注解,解析其它注解,作为 resultMapId 属性
} else if (isSelect) {
resultMapId = parseResultMap(method);
}

// 构建 MappedStatement 对象
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method), // 获得返回类型
resultSetType,
flushCache,
useCache,
// TODO gcode issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
options != null ? nullOrEmpty(options.resultSets()) : null);
}
}
  • <1> 处,调用 #getParameterType(Method method) 方法,获得参数的类型。详细解析,见 「2.6.1 getParameterType」
  • <2> 处,调用 #getLanguageDriver(Method method) 方法,获得 LanguageDriver 对象。详细解析,见 「2.6.2 getLanguageDriver」
  • <3> 处,调用 #getSqlSourceFromAnnotations(...) 方法,从注解中,获得 SqlSource 对象。详细解析,见 「2.6.3 getSqlSourceFromAnnotations」
  • <4> 处,获得各种属性。
  • <5> 处,获得 KeyGenerator 对象。
    • <5.1> 处,如果有 @SelectKey 注解,则调用 #handleSelectKeyAnnotation(...) 方法,处理 @@SelectKey 注解,生成对应的 SelectKey 对象。详细解析,见 「2.6.4 handleSelectKeyAnnotation」
    • <5.2> 处,如果无 @Options 注解,则根据全局配置处理,使用 Jdbc3KeyGenerator 或 NoKeyGenerator 单例。
    • <5.3> 处,如果有 @Options 注解,则使用该注解的配置处理。
    • <5.4> 处,非插入和更新语句,无需 KeyGenerator 对象,所以使用 NoKeyGenerator 单例。
  • <6> 处,初始化各种属性。
  • <7> 处,获得 resultMapId 编号字符串。
    • <7.1> 处,如果有 @ResultMap 注解,使用该注解为 resultMapId 属性。因为 @ResultMap 注解的作用,就是注解使用的结果集。
    • <7.2> 处,如果无 @ResultMap 注解,调用 #parseResultMap(Method method) 方法,解析其它注解,作为 resultMapId 属性。详细解析,见 「2.6.6 parseResultMap」

2.6.1 getParameterType

调用 #getParameterType(Method method) 方法,获得参数的类型。代码如下:

// MapperAnnotationBuilder.java

private Class<?> getParameterType(Method method) {
Class<?> parameterType = null;
// 遍历参数类型数组
// 排除 RowBounds 和 ResultHandler 两种参数
// 1. 如果是多参数,则是 ParamMap 类型
// 2. 如果是单参数,则是该参数的类型
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> currentParameterType : parameterTypes) {
if (!RowBounds.class.isAssignableFrom(currentParameterType) && !ResultHandler.class.isAssignableFrom(currentParameterType)) {
if (parameterType == null) {
parameterType = currentParameterType;
} else {
// issue #135
parameterType = ParamMap.class;
}
}
}
return parameterType;
}
  • 比较简单,根据是否为多参数,返回是 ParamMap 类型,还是单参数对应的类型。

2.6.2 getLanguageDriver

#getLanguageDriver(Method method) 方法,获得 LanguageDriver 对象。代码如下:

// MapperAnnotationBuilder.java

private LanguageDriver getLanguageDriver(Method method) {
// 解析 @Lang 注解,获得对应的类型
Lang lang = method.getAnnotation(Lang.class);
Class<? extends LanguageDriver> langClass = null;
if (lang != null) {
langClass = lang.value();
}
// 获得 LanguageDriver 对象
// 如果 langClass 为空,即无 @Lang 注解,则会使用默认 LanguageDriver 类型
return assistant.getLanguageDriver(langClass);
}

2.6.3 getSqlSourceFromAnnotations

#getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) 方法,从注解中,获得 SqlSource 对象。代码如下:

// MapperAnnotationBuilder.java

private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
try {
// <1.1> <1.2> 获得方法上的 SQL_ANNOTATION_TYPES 和 SQL_PROVIDER_ANNOTATION_TYPES 对应的类型
Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
// <2> 如果 SQL_ANNOTATION_TYPES 对应的类型非空
if (sqlAnnotationType != null) {
// 如果 SQL_PROVIDER_ANNOTATION_TYPES 对应的类型非空,则抛出 BindingException 异常,因为冲突了。
if (sqlProviderAnnotationType != null) {
throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
}
// <2.1> 获得 SQL_ANNOTATION_TYPES 对应的注解
Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
// <2.2> 获得 value 属性
final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
// <2.3> 创建 SqlSource 对象
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
// <3> 如果 SQL_PROVIDER_ANNOTATION_TYPES 对应的类型非空
} else if (sqlProviderAnnotationType != null) {
// <3.1> 获得 SQL_PROVIDER_ANNOTATION_TYPES 对应的注解
Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
// <3.2> 创建 ProviderSqlSource 对象
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
}
// <4> 返回空
return null;
} catch (Exception e) {
throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
}
}
  • <1.1> 处,调用 #getSqlAnnotationType(Method method) 方法,获得方法上的 SQL_ANNOTATION_TYPES 类型的注解类型。详细解析,见 「2.6.3.1 getSqlAnnotationType」
  • <1.2> 处,调用 #getSqlProviderAnnotationType(Method method) 方法,获得方法上的 SQL_PROVIDER_ANNOTATION_TYPES 类型的注解类型。详细解析,见 「2.6.3.2 getSqlAnnotationType」
  • <2> 处,如果 SQL_ANNOTATION_TYPES 对应的类型非空的情况,例如 @Insert 注解:
    • <2.1> 处,获得 SQL_ANNOTATION_TYPES 对应的注解。
    • <2.2> 处,通过反射,获得对应的 value 属性。因为这里的注解类有多种,所以只好通过反射方法来获取该属性。
    • <2.3> 处,调用 #buildSqlSourceFromStrings(...) 方法,创建 SqlSource 对象。详细解析,见 「2.6.3.3 buildSqlSourceFromStrings」
  • <3> 处,如果 SQL_PROVIDER_ANNOTATION_TYPES 对应的类型非空的情况,例如 @InsertProvider 注解:
    • <3.1> 处,获得 SQL_PROVIDER_ANNOTATION_TYPES 对应的注解。
    • <3.2> 处,创建 ProviderSqlSource 对象。详细解析,见后续的文章。
  • <4> 处,没有空,没有符合的注解。

2.6.3.1 getSqlAnnotationType

#getSqlAnnotationType(Method method) 方法,获得方法上的 SQL_ANNOTATION_TYPES 类型的注解类型。代码如下:

// MapperAnnotationBuilder.java

private Class<? extends Annotation> getSqlAnnotationType(Method method) {
return chooseAnnotationType(method, SQL_ANNOTATION_TYPES);
}

/**
* 获得符合指定类型的注解类型
*
* @param method 方法
* @param types 指定类型
* @return 查到的注解类型
*/
private Class<? extends Annotation> chooseAnnotationType(Method method, Set<Class<? extends Annotation>> types) {
for (Class<? extends Annotation> type : types) {
Annotation annotation = method.getAnnotation(type);
if (annotation != null) {
return type;
}
}
return null;
}

2.6.3.2 getSqlProviderAnnotationType

#getSqlProviderAnnotationType(Method method) 方法,获得方法上的 SQL_ANNOTATION_TYPES 类型的注解类型。代码如下:

// MapperAnnotationBuilder.java

private Class<? extends Annotation> getSqlProviderAnnotationType(Method method) {
return chooseAnnotationType(method, SQL_PROVIDER_ANNOTATION_TYPES);
}

2.6.3.3 buildSqlSourceFromStrings

#buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) 方法,创建 SqlSource 对象。代码如下:

// MapperAnnotationBuilder.java

private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
// <1> 拼接 SQL
final StringBuilder sql = new StringBuilder();
for (String fragment : strings) {
sql.append(fragment);
sql.append(" ");
}
// <2> 创建 SqlSource 对象
return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass);
}
  • <1> 处,拼接 SQL ,使用 " " 空格分隔。因为,@Select 等注解,value 属性是个数组
  • <2> 处,调用 LanguageDriver#createSqlSource(Configuration configuration, String script, Class<?> parameterType) 方法,创建 SqlSource 对象。详细解析,见后续文章。

2.6.4 handleSelectKeyAnnotation

#handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class<?> parameterTypeClass, LanguageDriver languageDriver) 方法,处理 @@SelectKey 注解,生成对应的 SelectKey 对象。代码如下:

// MapperAnnotationBuilder.java

private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
// 获得各种属性和对应的类
String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
Class<?> resultTypeClass = selectKeyAnnotation.resultType();
StatementType statementType = selectKeyAnnotation.statementType();
String keyProperty = selectKeyAnnotation.keyProperty();
String keyColumn = selectKeyAnnotation.keyColumn();
boolean executeBefore = selectKeyAnnotation.before();

// defaults
// 创建 MappedStatement 需要用到的默认值
boolean useCache = false;
KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
Integer fetchSize = null;
Integer timeout = null;
boolean flushCache = false;
String parameterMap = null;
String resultMap = null;
ResultSetType resultSetTypeEnum = null;

// 创建 SqlSource 对象
SqlSource sqlSource = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver);
SqlCommandType sqlCommandType = SqlCommandType.SELECT;

// 创建 MappedStatement 对象
assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum,
flushCache, useCache, false,
keyGenerator, keyProperty, keyColumn, null, languageDriver, null);

// 获得 SelectKeyGenerator 的编号,格式为 `${namespace}.${id}`
id = assistant.applyCurrentNamespace(id, false);
// 获得 MappedStatement 对象
MappedStatement keyStatement = configuration.getMappedStatement(id, false);
// 创建 SelectKeyGenerator 对象,并添加到 configuration 中
SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
configuration.addKeyGenerator(id, answer);
return answer;
}

2.6.5 getSqlCommandType

#getSqlCommandType(Method method) 方法,获得方法对应的 SQL 命令类型。代码如下:

// MapperAnnotationBuilder.java

private SqlCommandType getSqlCommandType(Method method) {
// 获得符合 SQL_ANNOTATION_TYPES 类型的注解类型
Class<? extends Annotation> type = getSqlAnnotationType(method);

if (type == null) {
// 获得符合 SQL_PROVIDER_ANNOTATION_TYPES 类型的注解类型
type = getSqlProviderAnnotationType(method);

// 找不到,返回 SqlCommandType.UNKNOWN
if (type == null) {
return SqlCommandType.UNKNOWN;
}

// 转换成对应的枚举
if (type == SelectProvider.class) {
type = Select.class;
} else if (type == InsertProvider.class) {
type = Insert.class;
} else if (type == UpdateProvider.class) {
type = Update.class;
} else if (type == DeleteProvider.class) {
type = Delete.class;
}
}

// 转换成对应的枚举
return SqlCommandType.valueOf(type.getSimpleName().toUpperCase(Locale.ENGLISH));
}

2.6.6 parseResultMap

#parseResultMap(Method method) 方法,解析其它注解,返回 resultMapId 属性。代码如下:

// MapperAnnotationBuilder.java

private String parseResultMap(Method method) {
// <1> 获得返回类型
Class<?> returnType = getReturnType(method);
// <2> 获得 @ConstructorArgs、@Results、@TypeDiscriminator 注解
ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);
Results results = method.getAnnotation(Results.class);
TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
// <3> 生成 resultMapId
String resultMapId = generateResultMapName(method);
// <4> 生成 ResultMap 对象
applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
return resultMapId;
}
  • <1> 处,调用 #getReturnType(Method method) 方法,获得返回类型。详细解析,见 「2.6.6.1 getReturnType」
  • <2> 处,获得 @ConstructorArgs@Results@TypeDiscriminator 注解。
  • <3> 处,调用 #generateResultMapName(Method method) 方法,生成 resultMapId 。详细解析,见 「2.6.6.2 generateResultMapName」
  • <4> 处,调用 #argsIf(ConstructorArgs args) 方法,获得 @ConstructorArgs 注解的 @Arg[] 数组。代码如下:

    // MapperAnnotationBuilder.java

    private Arg[] argsIf(ConstructorArgs args) {
    return args == null ? new Arg[0] : args.value();
    }
  • <4> 处,调用 #resultsIf((Results results) 方法,获得 @(Results 注解的 @Result[] 数组。

    // MapperAnnotationBuilder.java

    private Result[] resultsIf(Results results) {
    return results == null ? new Result[0] : results.value();
    }
  • <4> 处,调用 #applyResultMap(...) 方法,生成 ResultMap 对象。详细解析,见 「2.6.6.3 applyResultMap」

2.6.6.1 getReturnType

#getReturnType(Method method) 方法,获得返回类型。代码如下:

// MapperAnnotationBuilder.java

private Class<?> getReturnType(Method method) {
// 获得方法的返回类型
Class<?> returnType = method.getReturnType();
// 解析成对应的 Type
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
// 如果 Type 是 Class ,普通类
if (resolvedReturnType instanceof Class) {
returnType = (Class<?>) resolvedReturnType;
// 如果是数组类型,则使用 componentType
if (returnType.isArray()) {
returnType = returnType.getComponentType();
}
// gcode issue #508
// 如果返回类型是 void ,则尝试使用 @ResultType 注解
if (void.class.equals(returnType)) {
ResultType rt = method.getAnnotation(ResultType.class);
if (rt != null) {
returnType = rt.value();
}
}
// 如果 Type 是 ParameterizedType ,泛型
} else if (resolvedReturnType instanceof ParameterizedType) {
// 获得泛型 rawType
ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
// 如果是 Collection 或者 Cursor 类型时
if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {
// 获得 <> 中实际类型
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
// 如果 actualTypeArguments 的大小为 1 ,进一步处理
if (actualTypeArguments != null && actualTypeArguments.length == 1) {
Type returnTypeParameter = actualTypeArguments[0];
// 如果是 Class ,则直接使用 Class
if (returnTypeParameter instanceof Class<?>) {
returnType = (Class<?>) returnTypeParameter;
// 如果是 ParameterizedType ,则获取 <> 中实际类型
} else if (returnTypeParameter instanceof ParameterizedType) {
// (gcode issue #443) actual type can be a also a parameterized type
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
// 如果是泛型数组类型,则获得 genericComponentType 对应的类
} else if (returnTypeParameter instanceof GenericArrayType) {
Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
// (gcode issue #525) support List<byte[]>
returnType = Array.newInstance(componentType, 0).getClass();
}
}
// 如果有 @MapKey 注解,并且是 Map 类型
} else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
// (gcode issue 504) Do not look into Maps if there is not MapKey annotation
// 获得 <> 中实际类型
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
// 如果 actualTypeArguments 的大小为 2 ,进一步处理。
// 为什么是 2 ,因为 Map<K, V> 呀,有 K、V 两个泛型
if (actualTypeArguments != null && actualTypeArguments.length == 2) {
// 处理 V 泛型
Type returnTypeParameter = actualTypeArguments[1];
// 如果 V 泛型为 Class ,则直接使用 Class
if (returnTypeParameter instanceof Class<?>) {
returnType = (Class<?>) returnTypeParameter;
// 如果 V 泛型为 ParameterizedType ,则获取 <> 中实际类型
} else if (returnTypeParameter instanceof ParameterizedType) {
// (gcode issue 443) actual type can be a also a parameterized type
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
}
}
// 如果是 Optional 类型时
} else if (Optional.class.equals(rawType)) {
// 获得 <> 中实际类型
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
// 因为是 Optional<T> 类型,所以 actualTypeArguments 数组大小是一
Type returnTypeParameter = actualTypeArguments[0];
// 如果 <T> 泛型为 Class ,则直接使用 Class
if (returnTypeParameter instanceof Class<?>) {
returnType = (Class<?>) returnTypeParameter;
}
}
}

return returnType;
}
  • 方法非常的长,主要是进一步处理方法的返回类型 Method.returnType ,例如是数组类型、泛型类型、有 @MapKey 注解,或是 Optional 类型,等等情况。
  • 胖友大体看看就好,也不需要看的特别细。等回过头有需要,在详细看。

2.6.6.2 generateResultMapName

#generateResultMapName(Method method) 方法,生成 resultMapId 。代码如下:

// MapperAnnotationBuilder.java

private String generateResultMapName(Method method) {
// 第一种情况,已经声明
// 如果有 @Results 注解,并且有设置 id 属性,则直接返回。格式为:`${type.name}.${Results.id}` 。
Results results = method.getAnnotation(Results.class);
if (results != null && !results.id().isEmpty()) {
return type.getName() + "." + results.id();
}
// 第二种情况,自动生成
// 获得 suffix 前缀,相当于方法参数构成的签名
StringBuilder suffix = new StringBuilder();
for (Class<?> c : method.getParameterTypes()) {
suffix.append("-");
suffix.append(c.getSimpleName());
}
if (suffix.length() < 1) {
suffix.append("-void");
}
// 拼接返回。格式为 `${type.name}.${method.name}${suffix}` 。
return type.getName() + "." + method.getName() + suffix;
}
  • 分成两种情况:已经声明和自动生成。
  • 代码比较简单,胖友自己瞅瞅。

2.6.6.3 applyResultMap

#applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) 方法,创建 ResultMap 对象。代码如下:

// MapperAnnotationBuilder.java

private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) {
// <1> 创建 ResultMapping 数组
List<ResultMapping> resultMappings = new ArrayList<>();
// <2> 将 @Arg[] 注解数组,解析成对应的 ResultMapping 对象们,并添加到 resultMappings 中。
applyConstructorArgs(args, returnType, resultMappings);
// <3> 将 @Result[] 注解数组,解析成对应的 ResultMapping 对象们,并添加到 resultMappings 中。
applyResults(results, returnType, resultMappings);
// <4> 创建 Discriminator 对象
Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);
// TODO add AutoMappingBehaviour
// <5> ResultMap 对象
assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null);
// <6> 创建 Discriminator 的 ResultMap 对象们
createDiscriminatorResultMaps(resultMapId, returnType, discriminator);
}
  • <1> 处,创建 ResultMapping 数组。
  • <2> 处,调用 #applyConstructorArgs(...) 方法,将 @Arg[] 注解数组,解析成对应的 ResultMapping 对象们,并添加到 resultMappings 中。详细解析,见 「2.6.6.3.1 applyConstructorArgs」
  • <3> 处,调用 #applyResults(...) 方法,将 @Result[] 注解数组,解析成对应的 ResultMapping 对象们,并添加到 resultMappings 中。 详细解析,见 「2.6.6.3.2 applyResults」
  • <4> 处,调用 #applyDiscriminator(...) 方法,创建 Discriminator 对象。详细解析,见 「2.6.6.3.3 applyDiscriminator」
  • <5> 处,调用 MapperAnnotationBuilder#addResultMap(...) 方法,创建 ResultMap 对象。
  • <6> 处,调用 #createDiscriminatorResultMaps(...) 方法,创建 Discriminator 的 ResultMap 对象们。详细解析,见 「2.6.6.3.4 createDiscriminatorResultMaps」
  • 看完上述逻辑后,你会发现该方法,和 XMLMapperBuilder#resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) 方法是类似的。所以,胖友后续可以自己去回味下。
2.6.6.3.1 applyConstructorArgs

#applyConstructorArgs(...) 方法,将 @Arg[] 注解数组,解析成对应的 ResultMapping 对象们,并添加到 resultMappings 中。代码如下:

// MapperAnnotationBuilder.java

private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
// 遍历 @Arg[] 数组
for (Arg arg : args) {
// 创建 ResultFlag 数组
List<ResultFlag> flags = new ArrayList<>();
flags.add(ResultFlag.CONSTRUCTOR);
if (arg.id()) {
flags.add(ResultFlag.ID);
}
@SuppressWarnings("unchecked")
// 获得 TypeHandler 乐
Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
(arg.typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler());
// 将当前 @Arg 注解构建成 ResultMapping 对象
ResultMapping resultMapping = assistant.buildResultMapping(
resultType,
nullOrEmpty(arg.name()),
nullOrEmpty(arg.column()),
arg.javaType() == void.class ? null : arg.javaType(),
arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(),
nullOrEmpty(arg.select()),
nullOrEmpty(arg.resultMap()),
null,
nullOrEmpty(arg.columnPrefix()),
typeHandler,
flags,
null,
null,
false);
// 添加到 resultMappings 中
resultMappings.add(resultMapping);
}
}
2.6.6.3.2 applyResults

#applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) 方法,将 @Result[] 注解数组,解析成对应的 ResultMapping 对象们,并添加到 resultMappings 中。代码如下:

// MapperAnnotationBuilder.java

private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
// 遍历 @Result[] 数组
for (Result result : results) {
// 创建 ResultFlag 数组
List<ResultFlag> flags = new ArrayList<>();
if (result.id()) {
flags.add(ResultFlag.ID);
}
@SuppressWarnings("unchecked")
// 获得 TypeHandler 类
Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
((result.typeHandler() == UnknownTypeHandler.class) ? null : result.typeHandler());
// 构建 ResultMapping 对象
ResultMapping resultMapping = assistant.buildResultMapping(
resultType,
nullOrEmpty(result.property()),
nullOrEmpty(result.column()),
result.javaType() == void.class ? null : result.javaType(),
result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(),
hasNestedSelect(result) ? nestedSelectId(result) : null, // <1.1> <1.2>
null,
null,
null,
typeHandler,
flags,
null,
null,
isLazy(result)); // <2>
// 添加到 resultMappings 中
resultMappings.add(resultMapping);
}
}
  • 从实现逻辑上,我们会发现,和 XMLMapperBuilder#buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) 方法是一致的。所以就不重复解析了,胖友可以看看 《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置》「2.3.3.3 buildResultMappingFromContext」
  • <1.1> 处,调用 #hasNestedSelect(Result result) 方法,判断是否有内嵌的查询。代码如下:

    // MapperAnnotationBuilder.java

    private boolean hasNestedSelect(Result result) {
    if (result.one().select().length() > 0 && result.many().select().length() > 0) {
    throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
    }
    // 判断有 @One 或 @Many 注解
    return result.one().select().length() > 0 || result.many().select().length() > 0;
    }
  • <1.2> 处,调用 #nestedSelectId((Result result) 方法,调用 #nestedSelectId(Result result) 方法,获得内嵌的查询编号。代码如下:

    // MapperAnnotationBuilder.java

    private String nestedSelectId(Result result) {
    // 先获得 @One 注解
    String nestedSelect = result.one().select();
    // 获得不到,则再获得 @Many
    if (nestedSelect.length() < 1) {
    nestedSelect = result.many().select();
    }
    // 获得内嵌查询编号,格式为 `{type.name}.${select}`
    if (!nestedSelect.contains(".")) {
    nestedSelect = type.getName() + "." + nestedSelect;
    }
    return nestedSelect;
    }
  • <2> 处,调用 #isLazy(Result result) 方法,判断是否懒加载。代码如下:

    // MapperAnnotationBuilder.java

    private boolean isLazy(Result result) {
    // 判断是否开启懒加载
    boolean isLazy = configuration.isLazyLoadingEnabled();
    // 如果有 @One 注解,则判断是否懒加载
    if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) {
    isLazy = result.one().fetchType() == FetchType.LAZY;
    // 如果有 @Many 注解,则判断是否懒加载
    } else if (result.many().select().length() > 0 && FetchType.DEFAULT != result.many().fetchType()) {
    isLazy = result.many().fetchType() == FetchType.LAZY;
    }
    return isLazy;
    }
    • 根据全局是否懒加载 + @One@Many 注解。
2.6.6.3.3 applyDiscriminator

#applyDiscriminator(...) 方法,创建 Discriminator 对象。代码如下:

// MapperAnnotationBuilder.java

private Discriminator applyDiscriminator(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
if (discriminator != null) {
// 解析各种属性
String column = discriminator.column();
Class<?> javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType();
JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType();
@SuppressWarnings("unchecked")
// 获得 TypeHandler 类
Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
(discriminator.typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler());
// 遍历 @Case[] 注解数组,解析成 discriminatorMap 集合
Case[] cases = discriminator.cases();
Map<String, String> discriminatorMap = new HashMap<>();
for (Case c : cases) {
String value = c.value();
String caseResultMapId = resultMapId + "-" + value;
discriminatorMap.put(value, caseResultMapId);
}
// 创建 Discriminator 对象
return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);
}
return null;
}
2.6.6.3.4 createDiscriminatorResultMaps

#createDiscriminatorResultMaps(...) 方法,创建 Discriminator 的 ResultMap 对象们。代码如下:

// MapperAnnotationBuilder.java

private void createDiscriminatorResultMaps(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
if (discriminator != null) {
// 遍历 @Case 注解
for (Case c : discriminator.cases()) {
// 创建 @Case 注解的 ResultMap 的编号
String caseResultMapId = resultMapId + "-" + c.value();
// 创建 ResultMapping 数组
List<ResultMapping> resultMappings = new ArrayList<>();
// issue #136
// 将 @Arg[] 注解数组,解析成对应的 ResultMapping 对象们,并添加到 resultMappings 中。
applyConstructorArgs(c.constructArgs(), resultType, resultMappings);
// 将 @Result[] 注解数组,解析成对应的 ResultMapping 对象们,并添加到 resultMappings 中。
applyResults(c.results(), resultType, resultMappings);
// TODO add AutoMappingBehaviour
// 创建 ResultMap 对象
assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null);
}
}
}
  • 逻辑比较简单,遍历 @Case[] 注解数组,创建每个 @Case 对应的 ResultMap 对象。

2.7 MethodResolver

org.apache.ibatis.builder.annotation.MethodResolver ,注解方法的处理器。代码如下:

// MethodResolver.java

public class MethodResolver {

/**
* MapperAnnotationBuilder 对象
*/
private final MapperAnnotationBuilder annotationBuilder;
/**
* Method 方法
*/
private final Method method;

public MethodResolver(MapperAnnotationBuilder annotationBuilder, Method method) {
this.annotationBuilder = annotationBuilder;
this.method = method;
}

public void resolve() {
// 执行注解方法的解析
annotationBuilder.parseStatement(method);
}

}
  • #resolve() 方法里,可以调用 MapperAnnotationBuilder#parseStatement(Method method) 方法,执行注解方法的解析。

2.8 parsePendingMethods

#parsePendingMethods() 方法,代码如下:

private void parsePendingMethods() {
// 获得 MethodResolver 集合,并遍历进行处理
Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
synchronized (incompleteMethods) {
Iterator<MethodResolver> iter = incompleteMethods.iterator();
while (iter.hasNext()) {
try {
// 执行解析
iter.next().resolve();
iter.remove();
iter.remove();
} catch (IncompleteElementException e) {
// This method is still missing a resource
}
}
}
}

666. 彩蛋

简单的小文一篇,实际就是 《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》《精尽 MyBatis 源码分析 —— MyBatis 初始化(三)之加载 Statement 配置》注解 版。

至此,MyBatis XML 配置和注解配置已经都解析完成了,当然,这个不包括 SQL 的解析。MyBatis 提供了强大的 动态 SQL 的功能,这也就是我们从下篇文章,会开始分享的内容。

总访客数 && 总访问量