# 精尽 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 void addMapper(Class type) { mapperRegistry.addMapper(type); } // MapperRegistry.java public void addMapper(Class 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> SQL_ANNOTATION_TYPES = new HashSet<>(); /** * SQL 操作提供者注解集合 */ private static final Set> 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 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 langClass = null; if (lang != null) { langClass = lang.value(); } // 获得 LanguageDriver 对象 // 如果 langClass 为空,即无 @Lang 注解,则会使用默认 LanguageDriver 类型 return assistant.getLanguageDriver(langClass); } ``` - 调用 `MapperBuilderAssistant#getLanguageDriver(Class 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 sqlAnnotationType = getSqlAnnotationType(method); Class 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 getSqlAnnotationType(Method method) { return chooseAnnotationType(method, SQL_ANNOTATION_TYPES); } /** * 获得符合指定类型的注解类型 * * @param method 方法 * @param types 指定类型 * @return 查到的注解类型 */ private Class chooseAnnotationType(Method method, Set> types) { for (Class 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 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 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 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 两个泛型 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 类型,所以 actualTypeArguments 数组大小是一 Type returnTypeParameter = actualTypeArguments[0]; // 如果 泛型为 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 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 additionalResultMappings)` 方法是类似的。所以,胖友后续可以自己去回味下。 ##### 2.6.6.3.1 applyConstructorArgs `#applyConstructorArgs(...)` 方法,将 `@Arg[]` 注解数组,解析成对应的 ResultMapping 对象们,并添加到 `resultMappings` 中。代码如下: ``` // MapperAnnotationBuilder.java private void applyConstructorArgs(Arg[] args, Class resultType, List resultMappings) { // 遍历 @Arg[] 数组 for (Arg arg : args) { // 创建 ResultFlag 数组 List flags = new ArrayList<>(); flags.add(ResultFlag.CONSTRUCTOR); if (arg.id()) { flags.add(ResultFlag.ID); } @SuppressWarnings("unchecked") // 获得 TypeHandler 乐 Class> typeHandler = (Class>) (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 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 resultMappings)` 方法,将 `@Result[]` 注解数组,解析成对应的 ResultMapping 对象们,并添加到 `resultMappings` 中。代码如下: ``` // MapperAnnotationBuilder.java private void applyResults(Result[] results, Class resultType, List resultMappings) { // 遍历 @Result[] 数组 for (Result result : results) { // 创建 ResultFlag 数组 List flags = new ArrayList<>(); if (result.id()) { flags.add(ResultFlag.ID); } @SuppressWarnings("unchecked") // 获得 TypeHandler 类 Class> typeHandler = (Class>) ((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 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> typeHandler = (Class>) (discriminator.typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler()); // 遍历 @Case[] 注解数组,解析成 discriminatorMap 集合 Case[] cases = discriminator.cases(); Map 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 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 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 incompleteMethods = configuration.getIncompleteMethods(); synchronized (incompleteMethods) { Iterator 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) 的功能,这也就是我们从下篇文章,会开始分享的内容。