1258 lines
53 KiB
Markdown
1258 lines
53 KiB
Markdown
# 精尽 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) 的功能,这也就是我们从下篇文章,会开始分享的内容。 |