1. 概述 本文接 《精尽 MyBatis 源码分析 —— MyBatis 初始化(三)之加载 Statement 配置》 一文,来分享 MyBatis 初始化的第 2 步的一部分,加载注解配置 。而这个部分的入口是 MapperAnnotationBuilder 。下面,我们一起来看看它的代码实现。
在 《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》 的 「3.3.13 mapperElement」 中,我们已经看到对 MapperAnnotationBuilder 的调用代码。代码如下:
public <T> void addMapper (Class<T> type) { mapperRegistry.addMapper(type); } public <T> void addMapper (Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry." ); } boolean loadCompleted = false ; try { knownMappers.put(type, new MapperProxyFactory<>(type)); MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true ; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
2. MapperAnnotationBuilder org.apache.ibatis.builder.annotation.MapperAnnotationBuilder
,Mapper 注解构造器,负责解析 Mapper 接口上的注解。
2.1 构造方法 private static final Set<Class<? extends Annotation>> SQL_ANNOTATION_TYPES = new HashSet<>();private static final Set<Class<? extends Annotation>> SQL_PROVIDER_ANNOTATION_TYPES = new HashSet<>();private final Configuration configuration;private final MapperBuilderAssistant assistant;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) { String resource = type.getName().replace('.' , '/' ) + ".java (best guess)" ; this .assistant = new MapperBuilderAssistant(configuration, resource); this .configuration = configuration; this .type = type; }
2.2 parse #parse()
方法,解析注解。代码如下:
public void parse () { String resource = type.toString(); if (!configuration.isResourceLoaded(resource)) { loadXmlResource(); configuration.addLoadedResource(resource); assistant.setCurrentNamespace(type.getName()); parseCache(); parseCacheRef(); Method[] methods = type.getMethods(); for (Method method : methods) { try { if (!method.isBridge()) { parseStatement(method); } } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new MethodResolver(this , method)); } } } parsePendingMethods(); }
2.3 loadXmlResource #loadXmlResource()
方法,加载对应的 XML Mapper 。代码如下:
private void loadXmlResource () { if (!configuration.isResourceLoaded("namespace:" + type.getName())) { String xmlResource = type.getName().replace('.' , '/' ) + ".xml" ; InputStream inputStream = type.getResourceAsStream("/" + xmlResource); if (inputStream == null ) { try { inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource); } catch (IOException e2) { } } 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
注解。代码如下:
private void parseCache () { CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class); if (cacheDomain != null ) { Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size(); Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval(); Properties props = convertToProperties(cacheDomain.properties()); assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props); } }
2.5 parseCacheRef #parseCacheRef()
方法,解析 @CacheNamespaceRef
注解。代码如下:
private void parseCacheRef () { CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class); if (cacheDomainRef != null ) { Class<?> refType = cacheDomainRef.value(); String refName = cacheDomainRef.name(); if (refType == void .class && refName.isEmpty()) { throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef" ); } if (refType != void .class && !refName.isEmpty()) { throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef" ); } String namespace = (refType != void .class) ? refType.getName() : refName; assistant.useCacheRef(namespace); } }
2.6 parseStatement #parseStatement(Method method)
方法,解析方法上的 SQL 操作相关的注解。代码如下:
void parseStatement (Method method) { Class<?> parameterTypeClass = getParameterType(method); LanguageDriver languageDriver = getLanguageDriver(method); SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver); if (sqlSource != null ) { 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; KeyGenerator keyGenerator; String keyProperty = null ; String keyColumn = null ; if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) { SelectKey selectKey = method.getAnnotation(SelectKey.class); if (selectKey != null ) { keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver); keyProperty = selectKey.keyProperty(); } else if (options == null ) { keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } else { keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; keyProperty = options.keyProperty(); keyColumn = options.keyColumn(); } } else { keyGenerator = NoKeyGenerator.INSTANCE; } 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 ; timeout = options.timeout() > -1 ? options.timeout() : null ; statementType = options.statementType(); resultSetType = options.resultSetType(); } String resultMapId = null ; 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(); } else if (isSelect) { resultMapId = parseResultMap(method); } assistant.addMappedStatement( mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout, null , parameterTypeClass, resultMapId, getReturnType(method), resultSetType, flushCache, useCache, false , keyGenerator, keyProperty, keyColumn, null , languageDriver, 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)
方法,获得参数的类型。代码如下:
private Class<?> getParameterType(Method method) { Class<?> parameterType = null ; Class<?>[] parameterTypes = method.getParameterTypes(); for (Class<?> currentParameterType : parameterTypes) { if (!RowBounds.class.isAssignableFrom(currentParameterType) && !ResultHandler.class.isAssignableFrom(currentParameterType)) { if (parameterType == null ) { parameterType = currentParameterType; } else { parameterType = ParamMap.class; } } } return parameterType; }
比较简单,根据是否为多参数,返回是 ParamMap 类型,还是单参数对应的类型。
2.6.2 getLanguageDriver #getLanguageDriver(Method method)
方法,获得 LanguageDriver 对象。代码如下:
private LanguageDriver getLanguageDriver (Method method) { Lang lang = method.getAnnotation(Lang.class); Class<? extends LanguageDriver> langClass = null ; if (lang != null ) { langClass = lang.value(); } return assistant.getLanguageDriver(langClass); }
2.6.3 getSqlSourceFromAnnotations #getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver)
方法,从注解中,获得 SqlSource 对象。代码如下:
private SqlSource getSqlSourceFromAnnotations (Method method, Class<?> parameterType, LanguageDriver languageDriver) { try { Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method); Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method); if (sqlAnnotationType != null ) { if (sqlProviderAnnotationType != null ) { throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName()); } Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType); final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value" ).invoke(sqlAnnotation); return buildSqlSourceFromStrings(strings, parameterType, languageDriver); } else if (sqlProviderAnnotationType != null ) { Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType); return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method); } 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
类型的注解类型。代码如下:
private Class<? extends Annotation> getSqlAnnotationType(Method method) { return chooseAnnotationType(method, SQL_ANNOTATION_TYPES); } 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
类型的注解类型。代码如下:
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 对象。代码如下:
private SqlSource buildSqlSourceFromStrings (String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) { final StringBuilder sql = new StringBuilder(); for (String fragment : strings) { sql.append(fragment); sql.append(" " ); } 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 对象。代码如下:
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(); 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 = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver); SqlCommandType sqlCommandType = SqlCommandType.SELECT; assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, false , keyGenerator, keyProperty, keyColumn, null , languageDriver, null ); id = assistant.applyCurrentNamespace(id, false ); MappedStatement keyStatement = configuration.getMappedStatement(id, false ); SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore); configuration.addKeyGenerator(id, answer); return answer; }
2.6.5 getSqlCommandType #getSqlCommandType(Method method)
方法,获得方法对应的 SQL 命令类型。代码如下:
private SqlCommandType getSqlCommandType (Method method) { Class<? extends Annotation> type = getSqlAnnotationType(method); if (type == null ) { type = getSqlProviderAnnotationType(method); 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
属性。代码如下:
private String parseResultMap (Method method) { Class<?> returnType = getReturnType(method); ConstructorArgs args = method.getAnnotation(ConstructorArgs.class); Results results = method.getAnnotation(Results.class); TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class); String resultMapId = generateResultMapName(method); 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[]
数组。代码如下:
private Arg[] argsIf(ConstructorArgs args) { return args == null ? new Arg[0 ] : args.value(); }
<4>
处,调用 #resultsIf((Results results)
方法,获得 @(Results
注解的 @Result[]
数组。
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)
方法,获得返回类型。代码如下:
private Class<?> getReturnType(Method method) { Class<?> returnType = method.getReturnType(); Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type); if (resolvedReturnType instanceof Class) { returnType = (Class<?>) resolvedReturnType; if (returnType.isArray()) { returnType = returnType.getComponentType(); } if (void .class.equals(returnType)) { ResultType rt = method.getAnnotation(ResultType.class); if (rt != null ) { returnType = rt.value(); } } } else if (resolvedReturnType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType; Class<?> rawType = (Class<?>) parameterizedType.getRawType(); if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); if (actualTypeArguments != null && actualTypeArguments.length == 1 ) { Type returnTypeParameter = actualTypeArguments[0 ]; if (returnTypeParameter instanceof Class<?>) { returnType = (Class<?>) returnTypeParameter; } else if (returnTypeParameter instanceof ParameterizedType) { returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType(); } else if (returnTypeParameter instanceof GenericArrayType) { Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType(); returnType = Array.newInstance(componentType, 0 ).getClass(); } } } else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); if (actualTypeArguments != null && actualTypeArguments.length == 2 ) { Type returnTypeParameter = actualTypeArguments[1 ]; if (returnTypeParameter instanceof Class<?>) { returnType = (Class<?>) returnTypeParameter; } else if (returnTypeParameter instanceof ParameterizedType) { returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType(); } } } else if (Optional.class.equals(rawType)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); Type returnTypeParameter = actualTypeArguments[0 ]; if (returnTypeParameter instanceof Class<?>) { returnType = (Class<?>) returnTypeParameter; } } } return returnType; }
方法非常的长,主要是进一步处理方法的返回类型 Method.returnType
,例如是数组类型、泛型类型、有 @MapKey
注解,或是 Optional 类型,等等情况。
胖友大体看看就好,也不需要看的特别细。等回过头有需要,在详细看。
2.6.6.2 generateResultMapName #generateResultMapName(Method method)
方法,生成 resultMapId
。代码如下:
private String generateResultMapName (Method method) { Results results = method.getAnnotation(Results.class); if (results != null && !results.id().isEmpty()) { return type.getName() + "." + results.id(); } StringBuilder suffix = new StringBuilder(); for (Class<?> c : method.getParameterTypes()) { suffix.append("-" ); suffix.append(c.getSimpleName()); } if (suffix.length() < 1 ) { suffix.append("-void" ); } return type.getName() + "." + method.getName() + suffix; }
分成两种情况:已经声明和自动生成。
代码比较简单,胖友自己瞅瞅。
2.6.6.3 applyResultMap #applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator)
方法,创建 ResultMap 对象。代码如下:
private void applyResultMap (String resultMapId, Class<?> returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) { List<ResultMapping> resultMappings = new ArrayList<>(); applyConstructorArgs(args, returnType, resultMappings); applyResults(results, returnType, resultMappings); Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator); assistant.addResultMap(resultMapId, returnType, null , disc, resultMappings, null ); 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
中。代码如下:
private void applyConstructorArgs (Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) { for (Arg arg : args) { List<ResultFlag> flags = new ArrayList<>(); flags.add(ResultFlag.CONSTRUCTOR); if (arg.id()) { flags.add(ResultFlag.ID); } @SuppressWarnings ("unchecked" ) Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (arg.typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler()); 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.add(resultMapping); } }
2.6.6.3.2 applyResults #applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings)
方法,将 @Result[]
注解数组,解析成对应的 ResultMapping 对象们,并添加到 resultMappings
中。代码如下:
private void applyResults (Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) { for (Result result : results) { List<ResultFlag> flags = new ArrayList<>(); if (result.id()) { flags.add(ResultFlag.ID); } @SuppressWarnings ("unchecked" ) Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) ((result.typeHandler() == UnknownTypeHandler.class) ? null : result.typeHandler()); 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 , null , null , null , typeHandler, flags, null , null , isLazy(result)); 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)
方法,判断是否有内嵌的查询。代码如下:
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" ); } return result.one().select().length() > 0 || result.many().select().length() > 0 ; }
<1.2>
处,调用 #nestedSelectId((Result result)
方法,调用 #nestedSelectId(Result result)
方法,获得内嵌的查询编号。代码如下:
private String nestedSelectId (Result result) { String nestedSelect = result.one().select(); if (nestedSelect.length() < 1 ) { nestedSelect = result.many().select(); } if (!nestedSelect.contains("." )) { nestedSelect = type.getName() + "." + nestedSelect; } return nestedSelect; }
<2>
处,调用 #isLazy(Result result)
方法,判断是否懒加载。代码如下:
private boolean isLazy (Result result) { boolean isLazy = configuration.isLazyLoadingEnabled(); if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) { isLazy = result.one().fetchType() == FetchType.LAZY; } 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 对象。代码如下:
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" ) Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (discriminator.typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler()); 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); } return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap); } return null ; }
2.6.6.3.4 createDiscriminatorResultMaps #createDiscriminatorResultMaps(...)
方法,创建 Discriminator 的 ResultMap 对象们。代码如下:
private void createDiscriminatorResultMaps (String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) { if (discriminator != null ) { for (Case c : discriminator.cases()) { String caseResultMapId = resultMapId + "-" + c.value(); List<ResultMapping> resultMappings = new ArrayList<>(); applyConstructorArgs(c.constructArgs(), resultType, resultMappings); applyResults(c.results(), resultType, resultMappings); assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null , resultMappings, null ); } } }
逻辑比较简单,遍历 @Case[]
注解数组,创建每个 @Case
对应的 ResultMap 对象。
2.7 MethodResolver org.apache.ibatis.builder.annotation.MethodResolver
,注解方法的处理器。代码如下:
public class MethodResolver { private final MapperAnnotationBuilder annotationBuilder; 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 () { 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) { } } } }
666. 彩蛋 简单的小文一篇,实际就是 《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》 和 《精尽 MyBatis 源码分析 —— MyBatis 初始化(三)之加载 Statement 配置》 的 注解 版。
至此,MyBatis XML 配置和注解配置已经都解析完成了,当然,这个不包括 SQL 的解析。MyBatis 提供了强大的 动态 SQL 的功能,这也就是我们从下篇文章,会开始分享的内容。