code-learning/mybatis/18-mybatis-MyBatis 初始化(四)之加载注解配置.md

1258 lines
53 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 精尽 MyBatis 源码分析 —— MyBatis 初始化(四)之加载注解配置
# 1. 概述
本文接 [《精尽 MyBatis 源码分析 —— MyBatis 初始化(三)之加载 Statement 配置》](http://svip.iocoder.cn/MyBatis/builder-package-3) 一文,来分享 MyBatis 初始化的第 2 步的一部分,**加载注解配置**。而这个部分的入口是 MapperAnnotationBuilder 。下面,我们一起来看看它的代码实现。
在 [《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》](http://svip.iocoder.cn/MyBatis/builder-package-2) 的 [「3.3.13 mapperElement」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 中,我们已经看到对 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」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<3>` 处,调用 `Configuration#addLoadedResource(String resource)` 方法,标记该 Mapper 接口已经加载过。
- `<4>` 处,调用 `MapperBuilderAssistant#setCurrentNamespace(String currentNamespace)` 方法,设置 `namespace` 属性。
- `<5>` 处,调用 `#parseCache()` 方法,`@CacheNamespace` 注解。详细解析,见 [「2.4 parseCache」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<6>` 处,调用 `#parseCacheRef()` 方法,`@CacheNamespaceRef` 注解。详细解析,见 [「2.5 parseCacheRef」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<7>` 处,遍历每个方法,解析其上的注解。
- `<7.1>` 处,调用 `#parseStatement(Method method)` 方法,执行解析每个方法的注解。详细解析,见 [「2.6 parseStatement」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<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);
}
```
- 关于 MethodResolver 类,详细解析,见 [「2.7 MethodResolver」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<8>` 处,调用 `#parsePendingMethods()` 方法,解析待定的方法。详细解析,见 [「2.8 parsePendingMethods」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
## 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;
}
```
- `<4>` 处,调用 `MapperBuilderAssistant#useNewCache(...)` 方法,创建 Cache 对象。在 [《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》](http://svip.iocoder.cn/MyBatis/builder-package-2) 的 [「3.4 useNewCache」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 中,已经详细解析。
## 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);
}
}
```
- `<1>` 处,获得**类**上的 `@CacheNamespaceRef` 注解。
- `<2>` 处,获得各种属性,进行校验,最终获得 `namespace` 属性。
- `<3>` 处,调用 `MapperBuilderAssistant#useCacheRef(String namespace)` 方法,获得指向的 Cache 对象。在 [《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》](http://svip.iocoder.cn/MyBatis/builder-package-2) 的 [「3.3 useCacheRef」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 中,已经详细解析。
## 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」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<2>` 处,调用 `#getLanguageDriver(Method method)` 方法,获得 LanguageDriver 对象。详细解析,见 [「2.6.2 getLanguageDriver」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<3>` 处,调用 `#getSqlSourceFromAnnotations(...)` 方法,从注解中,获得 SqlSource 对象。详细解析,见 [「2.6.3 getSqlSourceFromAnnotations」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- ```
<4>
```
处,获得各种属性。
-
- ```
<5>
```
处,获得 KeyGenerator 对象。
- `<5.1>` 处,如果有 `@SelectKey` 注解,则调用 `#handleSelectKeyAnnotation(...)` 方法,处理 `@@SelectKey` 注解,生成对应的 SelectKey 对象。详细解析,见 [「2.6.4 handleSelectKeyAnnotation」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<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」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
### 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);
}
```
- 调用 `MapperBuilderAssistant#getLanguageDriver(Class<? extends LanguageDriver> langClass)` 方法,获得 LanguageDriver 对象。在 [《精尽 MyBatis 源码分析 —— MyBatis 初始化(三)之加载 Statement 配置》](http://svip.iocoder.cn/MyBatis/builder-package-3) 的 [「2.4 getLanguageDriver」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 中,已经详细解析。
### 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」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<1.2>` 处,调用 `#getSqlProviderAnnotationType(Method method)` 方法,获得方法上的 `SQL_PROVIDER_ANNOTATION_TYPES` 类型的注解类型。详细解析,见 [「2.6.3.2 getSqlAnnotationType」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- ```
<2>
```
处,如果
```
SQL_ANNOTATION_TYPES
```
对应的类型非空的情况,例如
```
@Insert
```
注解:
- `<2.1>` 处,获得 `SQL_ANNOTATION_TYPES` 对应的注解。
- `<2.2>` 处,通过反射,获得对应的 `value` 属性。因为这里的注解类有多种,所以只好通过反射方法来获取该属性。
- `<2.3>` 处,调用 `#buildSqlSourceFromStrings(...)` 方法,创建 SqlSource 对象。详细解析,见 [「2.6.3.3 buildSqlSourceFromStrings」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- ```
<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;
}
```
- 从实现逻辑上,我们会发现,和 `XMLStatementBuilder#parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId)` 方法是**一致**的。所以就不重复解析了,胖友可以看看 [《精尽 MyBatis 源码分析 —— MyBatis 初始化(三)之加载 Statement 配置》](http://svip.iocoder.cn/MyBatis/builder-package-3) 的 [「2.5.2 parseSelectKeyNode」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
### 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」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<2>` 处,获得 `@ConstructorArgs`、`@Results`、`@TypeDiscriminator` 注解。
- `<3>` 处,调用 `#generateResultMapName(Method method)` 方法,生成 `resultMapId` 。详细解析,见 [「2.6.6.2 generateResultMapName」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<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」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
#### 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」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<3>` 处,调用 `#applyResults(...)` 方法,将 `@Result[]` 注解数组,解析成对应的 ResultMapping 对象们,并添加到 `resultMappings` 中。 详细解析,见 [「2.6.6.3.2 applyResults」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<4>` 处,调用 `#applyDiscriminator(...)` 方法,创建 Discriminator 对象。详细解析,见 [「2.6.6.3.3 applyDiscriminator」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<5>` 处,调用 `MapperAnnotationBuilder#addResultMap(...)` 方法,创建 ResultMap 对象。
- `<6>` 处,调用 `#createDiscriminatorResultMaps(...)` 方法,创建 Discriminator 的 ResultMap 对象们。详细解析,见 [「2.6.6.3.4 createDiscriminatorResultMaps」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- 看完上述逻辑后,你会发现该方法,和 `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);
}
}
```
- 从实现逻辑上,我们会发现,和 `XMLMapperBuilder#processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings)` 方法是**一致**的。所以就不重复解析了,胖友可以看看 [《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置》](http://svip.iocoder.cn/MyBatis/builder-package-2) 的 [「2.3.3.1 processConstructorElement」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
##### 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 映射配置》](http://svip.iocoder.cn/MyBatis/builder-package-2) 的 [「2.3.3.3 buildResultMappingFromContext」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
- `<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;
}
```
- 从实现逻辑上,我们会发现,和 `XMLMapperBuilder#processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings)` 方法是**一致**的。所以就不重复解析了,胖友可以看看 [《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置》](http://svip.iocoder.cn/MyBatis/builder-package-2) 的 [「2.3.3.2 processDiscriminatorElement」](https://svip.iocoder.cn/MyBatis/builder-package-4/#) 。
##### 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
}
}
}
}
```
- 《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》
「2.5 parsePendingXXX」
是一个思路。
- 1获得对应的集合2遍历集合执行解析3执行成功则移除出集合4执行失败忽略异常。
- 当然,实际上,此处还是可能有执行解析失败的情况,但是随着每一个 Mapper 接口对应的 MapperAnnotationBuilder 执行一次这些方法,逐步逐步就会被全部解析完。😈
# 666. 彩蛋
简单的小文一篇,实际就是 [《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》](http://svip.iocoder.cn/MyBatis/builder-package-2) 和 [《精尽 MyBatis 源码分析 —— MyBatis 初始化(三)之加载 Statement 配置》](http://svip.iocoder.cn/MyBatis/builder-package-3) 的 **注解** 版。
至此MyBatis XML 配置和注解配置已经都解析完成了,当然,这个不包括 SQL 的解析。MyBatis 提供了强大的 [动态 SQL](http://www.mybatis.org/mybatis-3/zh/dynamic-sql.html) 的功能,这也就是我们从下篇文章,会开始分享的内容。