mybatis之注解配置的解析过程分析

写在前面

本文主要是分析使用注解方式配置的mapper接口的解析过程,作为这篇文章的补充。想要搭建调试环境的话,可以参考这篇文章
想要系统学习的,可以参考这篇文章,重要!!!

1:入口

在重新回顾下,可能如下的测试代码:

String resource = "mybatis/mybatis-config-annotation.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// <202108101757>
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

<202108101757>处源码如下:

org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream)
public SqlSessionFactory build(InputStream inputStream) {
  // <202108101758>
  return build(inputStream, null, null);
}

<202108101758>处源码如下:

org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.util.Properties)
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
  // <202108101759>
  return build(inputStream, null, properties);
}

<202108101759>处源码如下:

org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    // <202108101800>
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ...snip...
  }
}

<202108101800>parser.parse()源码如下:

org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
public Configuration parse() {
  ...snip...
  // 解析全局配置文件中的configuration节点
  // <202108101801>
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

<202108101801>处是解析全局配置文件中的configuration节点,源码如下:

org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {
  try {
    // 解析全局配置文件中的properties节点
    propertiesElement(root.evalNode("properties")); 
    // 解析全局配置文件中的typeAliases节点
    typeAliasesElement(root.evalNode("typeAliases"));
    // 解析全局配置文件的plugins节点
    pluginElement(root.evalNode("plugins"));
    // 解析全局配置文件中的objectFactory节点
    objectFactoryElement(root.evalNode("objectFactory"));
    // 解析全局配置文件中的objectWrapperFactory节点
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    // 解析全局配置文件中的settings节点
    settingsElement(root.evalNode("settings"));
    // 解析全局配置文件中的environments节点
    environmentsElement(root.evalNode("environments")); 
    // 解析全局配置文件中的databseIdProvider节点
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    // 解析全局配置文件中的typeHandlers节点
    typeHandlerElement(root.evalNode("typeHandlers"));
    // 解析全局配置文件中的mappers节点,重要!!!
    // <202108101805>
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

<202108101805>处是重点中的重点,解析mapper映射信息可能是xml方式也可能是注解方式,源码如下:

org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement
private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      // package方式直接配置要扫描的包路径
      if ("package".equals(child.getName())) {
        // 获取通过name属性配置的包路径
        String mapperPackage = child.getStringAttribute("name");
        // 执行解析
        // <202108101808>
        configuration.addMappers(mapperPackage);
      } else {
        ...snip...
      }
    }
  }
}

<202108101808>处是对要扫描的包路径执行具体的解析,也就是我们要分析的入口,具体可以参考2:注解配置解析

2:注解配置解析

1:入口我们已经分析了调用的过程,这里就一起来具体看下,源码如下:

org.apache.ibatis.session.Configuration#addMappers(java.lang.String)
public void addMappers(String packageName) {
  mapperRegistry.addMappers(packageName);
}

一直执行到如下方法:

org.apache.ibatis.binding.MapperRegistry#addMappers(java.lang.String, java.lang.Class<?>)
public void addMappers(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
  // new ResolverUtil.IsA(superType):创建ResolverUtil的IsA内部内部类,并将superType作为参数,这里的值一般都是java.lang.Object
  // packageName:要查找的包路径
  // 总体就是从指定的包路径下查找执行类型的.class文件
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  // 获取查找到的class集合
  // <202108111702>
  Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
  // 循环依次处理每个类的注解信息(如果需要的话)
  for (Class<?> mapperClass : mapperSet) {
    // 处理注解,并添加映射mapper
    // <202108111740>
    addMapper(mapperClass);
  }
}

<202108111702>如下图是我本地的包含了测试包路径下的类
在这里插入图片描述
<202108111740>处源码如下:

org.apache.ibatis.binding.MapperRegistry#addMapper
public <T> void addMapper(Class<T> type) {
  // 必须是接口
  if (type.isInterface()) {
    // 如果是已经存在当前类型的mapper信息,则异常
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    // 加载完成标记,默认为false,代表没有加载完成
    boolean loadCompleted = false;
    try {
      // 添加当前类型的映射信息,防止重复加载
      knownMappers.put(type, new MapperProxyFactory<T>(type));
      // 创建用于解析注解类的MapperAnnotationBuilder
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      // 执行解析
      // <202108111747>
      parser.parse();
      // 修改是否加载完成标记
      loadCompleted = true;
    } finally {
      // 最终没有加载完成,则移除类型映射信息
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

<202108111747>处是执行解析,具体参考2.1:解析注解类

2.1:解析注解类

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse
public void parse() {
  // 获取接口对应的字符串
  // 如:interface yudaosourcecode.mybatis.annotation.TestMybatisAnnotationMapper
  String resource = type.toString();
  // 没有加载过才继续,其中加载过程的资源使用
  // protected final Set<String> loadedResources = new HashSet<String>();存储
  if (!configuration.isResourceLoaded(resource)) {
    // 加载对应的xml文件,因为可能存在同时使用注解和xml配置的方式
    // <202108111811>
    loadXmlResource();
    // 添加到已加载资源集合中,防止重复加载
    configuration.addLoadedResource(resource);
    // 设置namespace属性
    assistant.setCurrentNamespace(type.getName());
    // 解析@org.apache.ibatis.annotation.CacheNamespace注解
    // 该注解用于启用二级缓存,注意需要在全局配置文件的setting中设置“cacheEnabled=true”
    // <202108121039>
    parseCache();
    // 解析@org.apache.ibatis.anotion.CacheNamespaceRef注解
    // <202108121047>
    parseCacheRef();
    // 获取所有的方法,依次解析每个方法的注解信息
    Method[] methods = type.getMethods();
    // 依次解析每个方法的注解信息
    for (Method method : methods) {
      try {
        // 非桥接方法才处理,桥接方法是为了兼容jdk1.5之后的泛型而生成的方法
        if (!method.isBridge()) { 
          // 根据方法解析生成statement
          // <202108121135>
          parseStatement(method);
        }
      } catch (IncompleteElementException e) {
        // 发生异常,则添加到不完备的解析方法集合中
        // protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
        configuration.addIncompleteMethod(new MethodResolver(this, method));
      }
    }
  }
  // 重复解析未完成的方法解析工作
  parsePendingMethods();
}

<202108111811>处是解析xml的配置文件,详细可以参考这篇文章<202108121039>处是解析@org.apache.ibatis.annotatition.CacheNamespace注解,具体参考2.2:解析CacheNamespace注解<202108121047>处是解析@org.apache.ibatis.annotation.CacheNamespaceRef注解,具体参考2.3:解析@CacheNamespaceRef注解<202108121135>处是通过方法和其使用的注解信息生成statement,具体参考3:解析方法生成statement

2.2:解析@CacheNamespace注解

可能的配置如下:

@CacheNamespace(implementation = PerpetualCache.class, eviction = LruCache.class)
public interface TestMybatisAnnotationMapper { ...snip... }

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseCache
private void parseCache() {
   // 通过class类型对象获取CacheNamespace注解
   CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
   // 如果是标记了CahceNamespace注解
   if (cacheDomain != null) {
     // 添加缓存配置信息
     // <202108121044>
     assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), cacheDomain.flushInterval(), cacheDomain.size(), cacheDomain.readWrite(), null);
   }
 }

<202108121044>处是添加缓存配置信息,具体可以参考这篇文章中的2.1.2:解析cache标签

2.3:解析@CacheNamespaceRef注解

该注解是直接相当于是mapper xml配置文件中的<cache-ref/>标签,源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseCacheRef
private void parseCacheRef() {
  // 通过class类型获取@org.apache.ibatis.annotation.CacheNamespaceRef注解
  CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
  if (cacheDomainRef != null) {
    // 应用@org.apache.ibatis.annotation.CacheNamespaceRef注解
    // <202108121129>
    assistant.useCacheRef(cacheDomainRef.value().getName());
  }
}

<202108121129>处是应用引用的cache信息,具体可以参考这篇文章中的2.1.1:解析cache-ref

3:解析方法生成statement

可能的代码如下:

@Insert(value = "INSERT INTO `test_mybatis_annotation` (`myname`, `myage`) VALUES (#{myname}, #{myage})")
void insertOne(TestMybatisAnnotationDto testMybatisAnnotationDto);
@Select(value = "SELECT * FROM test_mybatis_annotation t WHERE t.`id`=#{id} ")
TestMybatisAnnotation findOne(int id);

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseStatement
void parseStatement(Method method) {
  // 获取参数类型,如方法:void insertOne(TestMybatisAnnotationDto testMybatisAnnotationDto);
  // 结果就是“class yudaosourcecode.mybatis.annotation.TestMybatisAnnotationDto”
  // <202108121322>
  Class<?> parameterTypeClass = getParameterType(method);
  // 获取方法上配置的语言驱动类LanguageDriver
  // <202108121336>
  LanguageDriver languageDriver = getLanguageDriver(method);
  // 获取SqlSource对象,这是一个封装sql信息的接口
  // <202108121554>
  SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
  if (sqlSource != null) {
    // 获取@org.apache.ibatis.annotation.Options注解
    // <2021-08-13 10:43:31>
    Options options = method.getAnnotation(Options.class);
    // 使用类全限定名称和方法名称的组合作为statement的ID
    final String mappedStatementId = type.getName() + "." + method.getName();
    Integer fetchSize = null;
    Integer timeout = null;
    // statementType默认使用org.apache.ibatis.mapping.StatementType.PREPARED
    StatementType statementType = StatementType.PREPARED;
    ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
    // 获取sql语句类型
    SqlCommandType sqlCommandType = getSqlCommandType(method);
    // 是否为查询
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    // 不是select则默认刷新缓存
    boolean flushCache = !isSelect;
    // 是select则默认使用缓存
    boolean useCache = isSelect;
	// 主键生成对象
    KeyGenerator keyGenerator;
    // 主键属性默认为id
    String keyProperty = "id";
    String keyColumn = null;
    // 是插入或者是更新
    if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
      // 获取@org.apache.ibatis.annotation.SelectKey注解信息,用于生成主键使用
      SelectKey selectKey = method.getAnnotation(SelectKey.class);
      if (selectKey != null) {
        // 根据@org.apache.ibatis.annotation.SelectKey注解信息生成keyGenerator对象
        // <2021-08-13 11:07:18>
        keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
        // 使用@org.apache.ibatis.annotation.SelectKey注解配置的主键属性值
        keyProperty = selectKey.keyProperty();
      // 如果是没有配置@org.apache.ibatis.annotation.SelectKey注解
      } else {
        // 如果是没有配置@org.apache.ibatis.annotation.Options注解
        if (options == null) {
          // 如果是全局参数配置了使用生成的主键,则使用Jdbc3KeyGenerator,否则使用NoKeyGenerator
          keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
        // 如果是配置了@org.apache.ibatis.annotation.Options注解
        } else {
          // 如果是@org.apache.ibatis.annotation.Options注解配置了使用生成主键则使用
          // Jdbc3KeyGenerator,否则使用NoKeyGenerator
          keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
          // 获取主键属性
          keyProperty = options.keyProperty();
          // 获取数据库列
          keyColumn = options.keyColumn();
        }
      }
    // 如果既不是插入操作,也不是更新操作,则使用NoKeyGenerator,即不需要生成主键
    } else {
      keyGenerator = new NoKeyGenerator();
    }
    // 如果是配置了@org.apache.ibatis.annotation.Options注解,则获取其配置的相关信息
    if (options != null) {
      flushCache = options.flushCache();
      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;
    // 从方法上获取@org.apache.ibatis.annotation.ResultMap注解信息,一般配置
    // @org.apache.ibatis.annotation.Results注解使用 
    ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
    // 如果是配置了@org.apache.ibatis.annotation.ResultMap注解
    if (resultMapAnnotation != null) {
      // 获取配置的value值
      String[] resultMaps = resultMapAnnotation.value();
      // 循环生成resultMap的ID值
      StringBuilder sb = new StringBuilder();
      for (String resultMap : resultMaps) {
        if (sb.length() > 0) sb.append(",");
        sb.append(resultMap);
      }
      resultMapId = sb.toString();
    // 如果是数据查询,即使用的注解是@org.apache.ibatis.annotation.Select
    } else if (isSelect) {
      // 解析其他注解,如构造函数相关注解@org.apache.ibatis.annotation.ConstructorArgs
      // <2021-08-13 13:44:29>
      resultMapId = parseResultMap(method);
    }
    // 创建MappedStatement,并添加到org.apache.ibatis.session.Configuration对象中
    // <2021-08-13 18:10:41>
    assistant.addMappedStatement(
        mappedStatementId,
        sqlSource,
        statementType,
        sqlCommandType,
        fetchSize,
        timeout,
        null,                         
        parameterTypeClass,
        resultMapId,    // ResultMapID
        getReturnType(method),
        resultSetType,
        flushCache,
        useCache,
        false,
        keyGenerator,
        keyProperty,
        keyColumn,
        null,
        languageDriver,
        null);
  }
}

<202108121322>处是获取方法的参数类型,具体参考3.1:获取参数类型<202108121336>处是获取语言驱动类LanguageDriver,具体参考3.2:获取语言驱动类<202108121554>处是获取SqlSource对象,具体参考3.3:获取SqlSource对象<2021-08-13 10:43:31>处是获取@org.apache.ibatis.annotaion.Options注解,该注解用于对statement进行一些配置,如果缓存,statement类型等,源码如下:

org.apache.ibatis.annotations.Options
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Options {
  // 是否使用缓存
  boolean useCache() default true;
  // 是否刷新缓存
  boolean flushCache() default false;
  // 返回结果集类型
  ResultSetType resultSetType() default ResultSetType.FORWARD_ONLY;
  // statement类型
  StatementType statementType() default StatementType.PREPARED;
  // 每次取回数据量
  int fetchSize() default -1;
  // 超时时长
  int timeout() default -1;
  // 是否使用生成的主键,true则会返回生成的主键,false则不返回
  boolean useGeneratedKeys() default false;
  // 主键对应的属性
  String keyProperty() default "id";
  // 主键对应的列
  String keyColumn() default "";
}

<2021-08-13 11:07:18>处是根据XXXProvider方法上配置的@org.apache.ibatis.annotation.SelectKey注解生成org.apache.ibatis.executor.keygen.KeyGenerator对象,具体参考3.4:生成KeyGenerator对象<2021-08-13 13:44:29>处是解析返回结果相关的其他注解,如构造函数相关注解@org.apache.ibatis.annotation.ConstructorArgs,具体参考3.5:解析返回结果相关注解<2021-08-13 18:10:41>处事创建statement信息对应的org.apache.ibatis.mapping.MappedStatement对象,详细可以参考这里

3.1:获取参数类型

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getParameterType
private Class<?> getParameterType(Method method) {
  // 结果类型
  Class<?> parameterType = null;
  // 获取参数类型数组
  Class<?>[] parameterTypes = method.getParameterTypes();
  for (int i = 0; i < parameterTypes.length; i++) {
    // 不是org.apache.ibatis.session.RowBounds类型并且不是org.apache.ibatis.session.ResultHandler类型
    if (!RowBounds.class.isAssignableFrom(parameterTypes[i]) && !ResultHandler.class.isAssignableFrom(parameterTypes[i])) {
      // 如果是当前结果参数类型还为null,则使用当前遍历到的参数类型作为结果
      if (parameterType == null) {
        parameterType = parameterTypes[i];
      } else {
        // 否则使用org.apache.ibatis.binding.MapperMethod.ParamMap.class作为结果
        // 这是mybatis扩展HashMap的一个子类,签名如下:
        // public static class ParamMap<V> extends HashMap<String, V>
        parameterType = ParamMap.class; // issue #135
      }
    }
  }
  // 返回结果
  return parameterType;
}

3.2:获取语言驱动类

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getLanguageDriver
private LanguageDriver getLanguageDriver(Method method) {
  // 获取@org.apache.ibatis.annotation.Lang注解
  Lang lang = method.getAnnotation(Lang.class);
  Class<?> langClass = null;
  // 如果配置了lang则获取配置的value值
  if (lang != null) {
    langClass = lang.value();
  }
  // 创建LanguageDriver类实例
  // <202108121422>
  return assistant.getLanguageDriver(langClass);
}

<202108121422>处是获取LanguageDriver对象,具体参考这里

3.3:获取SqlSource对象

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getSqlSourceFromAnnotations
private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
  try {
    // 获取statement的类型
    // <202108121636>
    Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
    // 获取@org.apache.ibatis.annotation.XXXProvider,如@org.apache.ibatis.annotation.InsertProvider,此类注解是获取sql语句的另外一种方式
    // <202108121759>
    Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
    // 优先处理使用了sql注解的情况
    if (sqlAnnotationType != null) {
      // 执行到此处说明提供了sql注解,判断如果是也提供了sqlProvider注解,则直接异常
      // 我觉得这里完全可以忽略即可,不知道为什么只允许一个
      if (sqlProviderAnnotationType != null) {
        throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
      }
      // 获取sql注解
      Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
      // 通过反射调用注解的value方法,获取配置的sql语句数组值,这里虽然是数组并非多个独立
      // 的sql语句,而是将一个sql语句拆分成多个部分,比如可以select一个部分,from一个部分
      final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
      // 通过配置的sql语句数组构建SqlSource对象
      // <2021-08-13 09:31:47>
      return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
    // 处理使用了sqlProvider的情况
    } else if (sqlProviderAnnotationType != null) {
      // 获取XXXProvideer的注解
      Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
      // 通过全局配置和注解信息创建ProviderSqlSource对象
      // <2021-08-13 10:02:52>
      return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);
    }
    // 既没有sql注解,也没有sqlProvider注解的情况,直接返回null
    return null;
  } catch (Exception e) {
    throw new BuilderException("Could not find value method on SQL annotation.  Cause: " + e, e);
  }
}

<202108121636>处是获取statement的类型,具体参考3.4.1:获取statement类型<202108121759>是通过XXXProvider获取sql语句,如下使用@org.apache.ibatis.annotation.Insert注解的方式:

@Insert(value = "INSERT INTO `test_mybatis_annotation` (`myname`, `myage`) VALUES (#{myname}, #{myage})")
void insertOne(TestMybatisAnnotationDto testMybatisAnnotationDto);

如果是使用XXXProvider的方式则可能如下:

public class MySelectSqlProvider {

    public String provideInsertSql() {
        String insertSql = "INSERT INTO `test_mybatis_annotation` (`myname`, `myage`) VALUES (#{myname}, #{myage})";
        // INSERT INTO `test_mybatis_annotation` (`myname`, `myage`) VALUES (#{myname}, #{myage})
        return insertSql;
    }
}

@InsertProvider(type = MySelectSqlProvider.class, method = "provideInsertSql")
void insertWithSelectSqlProvider(TestMybatisAnnotationDto testMybatisAnnotationDto);

效果是完全一样的,具体参考3.4.2:获取XXXProvider<2021-08-13 09:31:47>处是通过解析在注解中配置的sql语句数组来构建SqlSource对象,具体参考3.4.3:基于sql数组构建SqlSource<2021-08-13 10:02:52>处是创建XXXProvider注解方式的SqlSource,具体参考3.4.4:XXXProvider创建SqlSource

3.3.1:获取statement类型

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getSqlAnnotationType
private Class<? extends Annotation> getSqlAnnotationType(Method method) {
  // <202108121639>
  return chooseAnnotationType(method, sqlAnnotationTypes);
}

<202108121639>处sqlAnnotationTypes定义如下:

private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<Class<? extends Annotation>>();

其中数据的初始化是在构造函数中完成的,源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#MapperAnnotationBuilder
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
  ...snip...
  sqlAnnotationTypes.add(Select.class);
  sqlAnnotationTypes.add(Insert.class);
  sqlAnnotationTypes.add(Update.class);
  sqlAnnotationTypes.add(Delete.class);
  ...snip...
}

<202108121639>处源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#chooseAnnotationType
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;
    }
  }
  // 没有匹配的,则返回null,代表注解中不包含如下注解中的任何一个
  // @org.apache.ibatis.annotation.Select
  // @org.apache.ibatis.annotation.Insert
  // @org.apache.ibatis.annotation.Update
  // @org.apache.ibatis.annoation.Delete
  return null;
}
3.3.2:获取XXXProvider

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getSqlProviderAnnotationType
private Class<? extends Annotation> getSqlProviderAnnotationType(Method method) {
  // <2021-08-12 18:08:41>
  return chooseAnnotationType(method, sqlProviderAnnotationTypes);
}

<2021-08-12 18:08:41>sqlProviderAnnotationTypes定义如下:

private final Set<Class<? extends Annotation>> sqlProviderAnnotationTypes = new HashSet<Class<? extends Annotation>>();

其中数据的初始化是在构造函数中完成的,源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#MapperAnnotationBuilder
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
  ...snip...
  sqlProviderAnnotationTypes.add(SelectProvider.class);
  sqlProviderAnnotationTypes.add(InsertProvider.class);
  sqlProviderAnnotationTypes.add(UpdateProvider.class);
  sqlProviderAnnotationTypes.add(DeleteProvider.class);
}

<2021-08-12 18:08:41>处源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#chooseAnnotationType
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;
}
3.3.3:基于sql数组构建SqlSource

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#buildSqlSourceFromStrings
private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
  // 将sql语句片段数组组装成最终的sql语句
  final StringBuilder sql = new StringBuilder();
  for (String fragment : strings) {
    sql.append(fragment);
    sql.append(" ");
  }
  // 创建SQLSource对象
  // <2021-08-13 09:43:23>
  return languageDriver.createSqlSource(configuration, sql.toString(), parameterTypeClass);
}

<2021-08-13 09:43:23>处是通过方法中的参数,sql语句,配置信息创建SqlSource对象,源码如下:

org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, java.lang.String, java.lang.Class<?>)
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
  if (script.startsWith("<script>")) { // issue #3
    XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
    return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
  } else {
    script = PropertyParser.parse(script, configuration.getVariables()); // issue #127
    TextSqlNode textSqlNode = new TextSqlNode(script);
    if (textSqlNode.isDynamic()) {
      return new DynamicSqlSource(configuration, textSqlNode);
    } else {
      return new RawSqlSource(configuration, script, parameterType);
    }
  }
}

详细分析这里先留个TODO

3.3.4:XXXProvider创建SqlSource

源码如下:

org.apache.ibatis.builder.annotation.ProviderSqlSource#ProviderSqlSource
public ProviderSqlSource(Configuration config, Object provider) {
  // XXXProvider的方法名称,即用于获取sql语句的方法名称 
  String providerMethodName = null;
  try {
    this.sqlSourceParser = new SqlSourceBuilder(config);
    // 反射获取XXXProvider类的class类型
    this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
    // 反射获取XXXProvider的method的值
    providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
	// 遍历XXXProvider类的所有的方法
    for (Method m : this.providerType.getMethods()) {
      // 如果和method配置的方法名称一样,则可能是目标方法
      if (providerMethodName.equals(m.getName())) {
        // 参数个数小于2个,并且返回类型是String的,才可能是目标方法
        if (m.getParameterTypes().length < 2
            && m.getReturnType() == String.class) {
          // 当前方法作为暂时的目标方法,暂时的原因是,循环并没有停止,因此可能的才会作为最终的目标方法
          // 不知道为什么要这么实现
          this.providerMethod = m;
          // 如果是参数的个数是1个则说明是需要参数对象
          this.providerTakesParameterObject = m.getParameterTypes().length == 1;
        }
      }
    }
  } catch (Exception e) {
    throw new BuilderException("Error creating SqlSource for SqlProvider.  Cause: " + e, e);
  }
  // 如果是最终没有获取到目标方法,则说明配置的方法不存在,直接异常
  if (this.providerMethod == null) {
    throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
        + providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
  }
}

3.4:生成KeyGenerator对象

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#handleSelectKeyAnnotation
private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
  // 生成id,如xxx.yyy.Obj!selectKey
  String id= baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  // 获取resultType的class类型
  Class<?> resultTypeClass = selectKeyAnnotation.resultType();
  // 获取org.apache.ibatis.mapping.StatementType的枚举值
  StatementType statementType = selectKeyAnnotation.statementType();
  // 获取主键属性名称
  String keyProperty = selectKeyAnnotation.keyProperty();
  // 获取主键列名称
  String keyColumn = selectKeyAnnotation.keyColumn();
  // 是否在执行sql语句前赋值主键值
  boolean executeBefore = selectKeyAnnotation.before();

  // 各种默认值
  boolean useCache = false;
  KeyGenerator keyGenerator = new NoKeyGenerator();
  Integer fetchSize = null;
  Integer timeout = null;
  boolean flushCache = false;
  String parameterMap = null;
  String resultMap = null;
  ResultSetType resultSetTypeEnum = null;
  // 根据selectKey的sql语句生成SqlSource
  // <2021-08-13 11:32:04>
  SqlSource sqlSource = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver);
  // sql语句的类型
  SqlCommandType sqlCommandType = SqlCommandType.SELECT;
  // 创建MappedStament并添加org.apache.ibatis.session.Configuration对象中
  // <2021-08-13 11:36:25>
  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);
  // 获取已经创建完的org.apache.ibatis.mapping.MappedStatement
  MappedStatement keyStatement = configuration.getMappedStatement(id, false);
  // 创建SelectKeyGenerator对象
  SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
  // 添加当前statement使用的keygenerator
  configuration.addKeyGenerator(id, answer);
  // 返回keygenerator
  return answer;
}

<2021-08-13 11:32:04>处是根据sql语句和参数等信息生成SqlSource对象,具体参考3.4.3:基于sql数组构建SqlSource<2021-08-13 11:36:25>处是创建org.apache.ibatis.mapping.MappedStatement对象,详细可以参考这里

3.5:解析返回结果相关注解

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseResultMap
private String parseResultMap(Method method) {
  // 获取查询结果的返回类型,是普通的对象?集合?还是Map?
  // <2021-08-13 14:02:51>
  Class<?> returnType = getReturnType(method);
  // 如果配置了@org.apache.ibatis.annotation.ConstructorArgs注解,则查询结果
  // 通过构造函数封装为对象
  ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);
  // 如果是配置了@org.apache.ibatis.annotation.Results注解,则是先创建对象
  // 然后通过属性的读写方法设置属性值
  Results results = method.getAnnotation(Results.class);
  // 如果是配置了@org.apache.ibatis.annotation.TypeDiscirminator注解则会根据某属性的
  // 查询结果值动态的设置另外一个属性的值
  TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
  // 生成resultMap的ID
  // <2021-08-13 17:25:38>
  String resultMapId = generateResultMapName(method);
  // 生成ResultMap对象
  // <2021-08-13 17:45:20>
  applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
  return resultMapId;
}

<2021-08-13 14:02:51>处是获取查询结果的返回类型,具体参考3.5.1:获取查询结果类型<2021-08-13 17:25:38>处是生成resultMap的ID,具体参考3.5.2:生成ResultMap的ID值<2021-08-13 17:45:20>处argsIf(args)用于获取构造函数的参数信息值,源码如下:

private Arg[] argsIf(ConstructorArgs args) {
  returnargs == null ? new Arg[0] : args.value();
}

resultsIf(results)是获取配置的resule信息,源码如下:

private Result[] resultsIf(Results results) {
  return results == null ? new Result[0] : results.value();
}

<2021-08-13 17:45:20>处是生成ResultMap对象,具体参考3.5.3:生成ResultMap对象

3.5.1:获取查询结果类型

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getReturnType
private Class<?> getReturnType(Method method) {
  // 获取方法签名中返回类型
  Class<?> returnType = method.getReturnType();
  // 如果是方法签名的返回类型的void,则尝试使用@org.apache.ibatis.annotation.ResultType注解配置的值
  // 这里为什么方法的返回值是void,还要获取呢,因为获取还是要获取的,虽然不使用
  if (void.class.equals(returnType)) {
    // 获取@org.apache.ibatis.annotation.ResultType注解
    ResultType rt = method.getAnnotation(ResultType.class);
    // 如果是配置了@org.apache.ibatis.annotation.ResultType注解,则使用其value作为结果
    if (rt != null) {
      returnType = rt.value();
    } 
  // 如果是返回类型是java.lang.Collection集合类型的子类
  } else if (Collection.class.isAssignableFrom(returnType)) {
    // 获取返回的泛型的类型
    Type returnTypeParameter = method.getGenericReturnType();
    // 如果是java.lang.relect.ParameterizedType类型的,当时带有泛型的集合会用这种类型
    // 的接口来进行分装
    if (returnTypeParameter instanceof ParameterizedType) {
      // 获取泛型的类型信息,如List<String>,这这里的结果就是[java.lang.String.class]
      Type[] actualTypeArguments = ((ParameterizedType) returnTypeParameter).getActualTypeArguments();
      // 集合类型只处理有一个泛型参数信息的情况
      if (actualTypeArguments != null && actualTypeArguments.length == 1) {
        // 获取实际的泛型参数
        returnTypeParameter = actualTypeArguments[0];
        // 如果泛型的类型是java.lang.Class
        if (returnTypeParameter instanceof Class) {
          // 直接强转java.lang.Class作为结果
          returnType = (Class<?>) returnTypeParameter;
        // 这种情况是集合里也使用了带有泛型的集合,如List<Set<String>>
        } else if (returnTypeParameter instanceof ParameterizedType) { 
          // 直接返回原始类型,如List<Set<String>>,结果就是java.util.Set.class
          returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
        // 如果是泛型数组
        } else if (returnTypeParameter instanceof GenericArrayType) {
          // 如List<List<Integer>[]>,这里获取的结果就是java.util.List<java.lang.Integer>
          Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
          // 创建长度为0的数组实例并获取class,作为结果
          // (issue #525) support List<byte[]>
          returnType = Array.newInstance(componentType, 0).getClass(); 
        }
      }
    }
  // 如果是方法上使用了@org.apache.ibatis.annotation.MapKey注解,并且返回类型是java.util.Map的子类
  } else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(returnType)) {
    // 获取带有反省信息的返回类型如,Map<Integer, TestMybatisAnnotation>
    // 结果就是:java.util.Map<java.lang.Integer, yudaosourcecode.mybatis.annotation.TestMybatisAnnotation>
    Type returnTypeParameter = method.getGenericReturnType();
    // 如果是带有泛型的类型
    if (returnTypeParameter instanceof ParameterizedType) {
      // 获取实际的参数类型数组,如Map<Integer, TestMybatisAnnotation>
      // 这里的结果就是
      // 0 = {Class@271} "class java.lang.Integer"
      // 1 = {Class@1512} "class yudaosourcecode.mybatis.annotation.TestMybatisAnnotation"
      Type[] actualTypeArguments = ((ParameterizedType) returnTypeParameter).getActualTypeArguments();
      // 有实际泛型参数,并且参数长度为2,长度为2是因为Map有key,value
      if (actualTypeArguments != null && actualTypeArguments.length == 2) {
        // 第二个作为返回的参数
        returnTypeParameter = actualTypeArguments[1];
        // 如果是Class
        if (returnTypeParameter instanceof Class) {
          returnType = (Class<?>) returnTypeParameter;
        // 如果又是带有反省的参数,如Map<Integer, List<TestMybatisAnnotation>>
        // 这里的结果就是List<TestMybatisAnnotation>
        } else if (returnTypeParameter instanceof ParameterizedType) { 
          // 直接返回原始类型,作为结果类型,如Map<Integer, List<TestMybatisAnnotation>>
          // 这里的结果就是java.util.List.Class
          returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
        }
      }
    }
  }
  // 返回类型
  return returnType;
}
3.5.2:生成ResultMap的ID值

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#generateResultMapName
private String generateResultMapName(Method method) {
  StringBuilder suffix = new StringBuilder();
  // 根据参数类型生成后缀
  for (Class<?> c : method.getParameterTypes()) {
    suffix.append("-");
    suffix.append(c.getSimpleName());
  }
  // 如果是没有参数则拼上"-void"后缀
  if (suffix.length() < 1) {
    suffix.append("-void");
  }
  // "类名.方法名.后缀"作为最终结果
  return type.getName() + "." + method.getName() + suffix;
}
3.5.3:生成ResultMap对象

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#applyResultMap
private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) {
  List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
  // 应用构造函数参数信息
  // <2021-08-13 17:51:14>
  applyConstructorArgs(args, returnType, resultMappings);
  // 应用Results信息
  // <2021-08-13 18:00:14>
  applyResults(results, returnType, resultMappings);
  // 应用Discriminator
  // <2021-08-13 18:06:49>
  Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);
  assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null); // TODO add AutoMappingBehaviour
  createDiscriminatorResultMaps(resultMapId, returnType, discriminator);
}

<2021-08-13 17:51:14>处是应用构造函数参数信息,这种方式是使用构造函数来对属性赋值,具体参考3.5.3.1:应用构造函数参数<2021-08-13 18:00:14>处是应用Results注解的信息,这种方式是使用对象的属性的读写方法来进行赋值,具体参考3.5.3.2:应用results信息<2021-08-13 18:06:49>处是应用discriminator信息,具体参考3.5.3.3:应用discriminator信息

3.5.3.1:应用构造函数参数

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#applyConstructorArgs
private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
  // 依次遍历每一个参数
  for (Arg arg : args) {
    ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
    flags.add(ResultFlag.CONSTRUCTOR);
    if (arg.id()) flags.add(ResultFlag.ID);
    // 构造org.apache.ibatis.mapping.ResultMaping
    // 一个ResultMapping代表一个参数配置
    // <2021-08-13 17:59:47>
    ResultMapping resultMapping = assistant.buildResultMapping(
        resultType,
        null,
        nullOrEmpty(arg.column()),
        arg.javaType() == void.class ? null : arg.javaType(),
        arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(),
        nullOrEmpty(arg.select()),
        nullOrEmpty(arg.resultMap()),
        null,
        null,
        arg.typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler(),
        flags,
        null,
        null,
        false);
    // 添加ResultMapping
    resultMappings.add(resultMapping);
  }
}

<2021-08-13 17:59:47>详细可以参考这里

3.5.3.2:应用results信息

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#applyResults
private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
  // 循环处理每一个属性的配置
  for (Result result : results) {
    ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
    if (result.id()) flags.add(ResultFlag.ID);
    // <2021-08-13 18:04:04>
    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,
        result.typeHandler() == UnknownTypeHandler.class ? null : result.typeHandler(),
        flags,
        null,
        null,
        isLazy(result));
    resultMappings.add(resultMapping);
  }
}

<2021-08-13 18:04:04>处可以参考这里

3.5.3.3:应用discriminator信息

源码如下:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#applyDiscriminator
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();
    Class<? extends TypeHandler<?>> typeHandler = discriminator.typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler();
    Case[] cases = discriminator.cases();
    Map<String, String> discriminatorMap = new HashMap<String, String>();
    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;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值