mybatis之全局配置文件<mappers>标签解析

写在前面

这篇文章中我们分析了解析全局配置文件的过程,在其中的"5.1.10:mappers"分析了解析<mappers/>标签时解析对应mapper xml的过程,会解析成XMLMapperBuilder对象,但是并没有详细分析解析过程,本文一起来看下,作为补充,所做的工作其实就是加载mapper xml映射配置文件
想要系统学习的,可以参考这篇文章,重要!!!

1:入口

源码如下:

org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {
  try {
    ...snip...
    // <202107161610>
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

<202107161610>处源码如下:

org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement
private void mapperElement(XNode parent) throws Exception {
  if(parent != null) {
    for (XNode child : parent.getChildren()) {
      // 有package,如配置:
      /*
	  <mappers>
        <package name="yudaosourcecode.mybatis.mapper"/>
      </mappers>
	  */
      if ("package".equals(child.getName())) {
        // 获取name的值。即包名
        String mapperPackage = child.getStringAttribute("name");
        // <202108101735>
        configuration.addMappers(mapperPackage);
      } else {
        // 获取resource,如<mapper resource="mybatis/mapper/TestTypehandlerMapper.xml"/>
        String resource = child.getStringAttribute("resource");
        // 获取url
        String url = child.getStringAttribute("url");
        // 获取class
        String mapperClass = child.getStringAttribute("class");
        // 优先处理resource,此时为类路径资源
        if (resource != null && url == null && mapperClass == null) {
          ErrorContext.instance().resource(resource);
          // 获取资源
          InputStream inputStream = Resources.getResourceAsStream(resource);
          // 创建XMLMapperBuilder
          // <202107161617>
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          // 执行解析,解析完毕后,就知道mapper xml对应的接口类是哪个了
          // <202107161724>
          mapperParser.parse();
        } else if (resource == null && url != null && mapperClass == null) { // 处理url情况
          ErrorContext.instance().resource(url);
          // 根据url获取资源
          InputStream inputStream = Resources.getUrlAsStream(url);
          // 创建XMLMapperBuilder
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
          // 执行解析
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) { // 直接指定了class
     	  // 获取映射的mapper接口的class
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          // 添加映射
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

<202107161617>处构造函数函数源码如下:

public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
  // XPathParser:是一个基于xpath解析xml的工具,在mybatis的parsing包中,属于解析模块的一个功能
  // configuration.getVariables():在全局配置文件中通过<properties/>标签配置的信息
  // <202107161626>
  this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
      configuration, resource, sqlFragments);
}

<202107161626>处源码如下:

/*
parser:基于xpath解析xml的工具类
configuration:解析全局config配置文件得到的配置类
resource:mapper xml的位置
sqlFragments:存储<sql>标签配置的sql语句段的集合
*/
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
  super(configuration);
  // 创建MapperBuilderAssistant对象
  this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
  this.parser = parser;
  this.sqlFragments = sqlFragments;
  this.resource = resource;
}

<202107161724>处是执行解析,具体参考2:XMLMapperBuilder#parse<202108101735>是在全局配置文件中配置通过<package>直接配置要扫描的包路径,可能如下配置:

<mappers>
    <package name="yudaosourcecode.mybatis.annotation"/>
</mappers>

这种方式配置不仅仅会解析到mapper xml,也会解析到使用注解方式配置的mapper 接口此时就不需要mapper xml了,本文会分析到前者,关于后者可以参考这篇文章

2:XMLMapperBuilder#parse

源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
public void parse() {
  // <202107161747>
  if (!configuration.isResourceLoaded(resource)) {
    // <202107161755>
    // 解析<mapper/>节点
    configurationElement(parser.evalNode("/mapper"));
    // 标记资源已经加载过
    /*
	org.apache.ibatis.session.Configuration#addLoadedResource
	public void addLoadedResource(String resource) {
	   // protected final Set<String> loadedResources = new HashSet<String>();
	   loadedResources.add(resource);
    }
	*/
    configuration.addLoadedResource(resource);
    // 绑定命名空间到mapper接口的jdk动态代理实现类
   	// <202107301547>
    bindMapperForNamespace();
  }
  // 注意以下待定的意思是,在前面的解析中存在解析到一半失败的节点,可能是因为有些依赖节点没有解析到导致
  // 所以这里再次重复解析
  // 解析待定的resultMap节点
  parsePendingResultMaps();
  // 解析待定的cache-ref节点
  parsePendingChacheRefs();
  // 解析待定的statement节点
  parsePendingStatements();
}

<202107161747>处是判断mapp xml资源是否已经加载过,源码如下:

org.apache.ibatis.session.Configuration#isResourceLoaded
public boolean isResourceLoaded(String resource) {
 // protected final Set<String> loadedResources = new HashSet<String>();
 // 通过set集合存储已经加载的资源对应的位置信息
 return loadedResources.contains(resource);
}

<202107161755>处是解析mapper节点,具体参考2.1:mapper节点解析,<202107301547>处是绑定mapper xml的对应的命名空间到mapper接口具体的jdk动态代理,源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace
private void bindMapperForNamespace() {
  // 获取命名空间,其实就是mapper的接口名称,正常都是这样配置的
  Stringnamespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      // 反射获取绑定的class类型
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
    }
    if (boundType != null) {
      // 如果不存在当前mapper信息
      // protected MapperRegistry mapperRegistry = new MapperRegistry(this);
      if (!configuration.hasMapper(boundType)) {
        // 防止重复加载
        // protected final Set<String> loadedResources = new HashSet<String>();
        configuration.addLoadedResource("namespace:" + namespace);
        // 设置映射的类型
        // protected MapperRegistry mapperRegistry = new MapperRegistry(this);
        // 这里的boundType就是mapper接口的全限定名称,后续基于此来生成jdk动态代理实现类
        // 2021-09-09 17:47:57
        configuration.addMapper(boundType);
      }
    }
  }
}

2021-09-09 17:47:57处是生成mapper的jdk动态代理实现类,然后添加到映射注册器中,源码如下:

org.apache.ibatis.session.Configuration#addMapper
public <T> void addMapper(Class<T> type) {
  mapperRegistry.addMapper(type);
}

org.apache.ibatis.binding.MapperRegistry#addMapper
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 {
      // 创建接口的动态代理工厂类并添加映射信息
      // private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
      // 2021-09-09 17:47:57
      knownMappers.put(type, new MapperProxyFactory<T>(type));
      ...snip...
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

2021-09-09 17:47:57处MapperProxyFactory是用来生成mapper动态代理的工厂类,具体参考3:MapperProxyFactory

2.1:mapper节点解析

源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
  	// <202107171110>
    configurationElement(parser.evalNode("/mapper"));
    ...snip...
  }
  ...snip...
}

<202107171110>处源码如下:

private void configurationElement(XNode context) {
  try {
    // 获取命名空间
    // <mapper namespace="yudaosourcecode.mybatis.mapper.TestResultmapAndParametermapMapper"><mapper/>
    String namespace = context.getStringAttribute("namespace");
    // 强制要求有namespace,如果是没有设置则异常
    if (namespace.equals("")) {
  	  throw new BuilderException("Mapper's namespace cannot be empty");
    }
    // 设置当前的命名空间值到映射构建帮助类MapperBuilderAssistant中
    builderAssistant.setCurrentNamespace(namespace);
    // <202107171125>
    cacheRefElement(context.evalNode("cache-ref"));
    // 解析<cache/>标签
    // <202107181024>
    cacheElement(context.evalNode("cache"));
    // 解析parameterMap的参数映射
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    // <202107181124>
    // 解析返回结果的参数配置resultMap
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    // <202107301052>
    // 解析mapper下的sql标签
    sqlElement(context.evalNodes("/mapper/sql"));
    // <202107301409>
    // 解析增insert删delete改update查select标签
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
  }
}

<202107181024>处是解析<cache>标签,具体参考2.1.2:解析cache标签<202107181124>处是解析resultMap,具体参考2.1.4:解析resultMap节点<202107301052>处是解析sql标签,具体参考2.1.5:解析sql标签<202107301409>处是解析增删改查标签,具体参考2.1.6:解析增删改查标签
<202107171125>是解析<cache-ref>处源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#cacheRefElement
private void cacheRefElement(XNode context) {
  if (context != null) {
    // builderAssistant.getCurrentNamespace():当前mapper xml的namespace
    // context.getStringAttribute("namespace"),<cache-ref namepsace="xxx.yyy">
	// 节点中配置的namepsace
	// 将这个cache的引用关系添加到configuration的map
	// protected final Map<String, String> cacheRefMap = new HashMap<String, String>();中    
	configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
	// 使用参数builderAssistant,和当前的mapper xml的namespace创建
	// CacheRefResolver解析器对象
    CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
    try {
      // 解析cache-ref
      // <202107180955>
  	  cacheRefResolver.resolveCacheRef();
    } catch (IncompleteElementException e) {
  	  configuration.addIncompleteCacheRef(cacheRefResolver);
    }
  }
}

<202107180955>处参考2.1.1:解析cache-ref

2.1.1:解析cache-ref

源码如下:

org.apache.ibatis.builder.CacheRefResolver#resolveCacheRef
public Cache resolveCacheRef() {
	return assistant.useCacheRef(cacheRefNamespace);
}

org.apache.ibatis.builder.MapperBuilderAssistant#useCacheRef
public Cache useCacheRef(String namespace) {
  // 如果是引用cache的命名空间为null,则抛出异常,并告知需要namepsace
  // 属性的异常信息
  // 如写<cache-ref>但是没有指定namespace属性,就会触发这里的异常
  if (namespace== null) {
    throw new BuilderException("cache-ref element requires a namespace attribute.");
  }
  try {
    unresolvedCacheRef = true;
    // 从配置中通过命名空间获取cache,Cache对象都是以命名空间为单位的
    Cache cache = configuration.getCache(namespace);
    // 如果是根据命名空间没有获取到引用的缓存则抛出异常
    if (cache == null) {
      throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
    }
    currentCache = cache;
    unresolvedCacheRef = false;
    return cache;
  } catch (IllegalArgumentException e) {
    throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
  }
}
2.1.2:解析cache标签

源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#cacheElement
private void cacheElement(XNode context) throws Exception {
  if (context != null) {
    // 获取二级缓存使用的缓存类型,默认是PERPETUAL、即org.apache.ibatis.cache.impl.PerpetualCache(系统默认提供)
    // 也可以通过实现接口org.apache.ibatis.cache.Cache自定义
    String type = context.getStringAttribute("type", "PERPETUAL");
    // 解析class
    Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
    // 获取缓存的淘汰算法,默认LRU,也可以是FIFO,WEAK,SOFT
    String eviction = context.getStringAttribute("eviction", "LRU");
    // 获取淘汰算法对应的class
    Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
    // 获取缓存的刷新间隔,默认不刷新
    Long flushInterval = context.getLongAttribute("flushInterval");
    // 获取缓存的大小
    Integer size = context.getIntAttribute("size");
    // 获取缓存是否只读,只读则总是返回相同实例,但不安全,非只读返回不同实例
    // 但是安全,因此默认值给false,即非只读(注意:这里的只读并非真的只读,而是说总是一个对象,只能只读)
    boolean readWrite = !context.getBooleanAttribute("readOnly", false);
    // 获取所有的子节点信息,name->value
    Properties props = context.getChildrenAsProperties();
    // <202107181039>
    // 添加新缓存
    builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);
  }
}

<202107181039>处是添加新缓存,具体参考2.1.3:添加命名空间缓存.

2.1.3:添加命名空间缓存

源码如下:

org.apache.ibatis.builder.MapperBuilderAssistant#useNewCache
public Cache useNewCache(Class<? extends Cache> typeClass,
      Class<? extends Cache> evictionClass,
      Long flushInterval,
      Integer size,
      boolean readWrite,
      Properties props) {
  // 获取缓存类型的class,提供了则使用提供的,否则使用PerpetualCache
  typeClass = valueOrDefault(typeClass, PerpetualCache.class);
  // 淘汰算法的class,提供了则使用提供的,否则使用LruCache.class
  evictionClass = valueOrDefault(evictionClass, LruCache.class);
  // 通过cache构造器构造缓存对象
  Cache cache = new CacheBuilder(currentNamespace)
      .implementation(typeClass)
      .addDecorator(evictionClass)
      .clearInterval(flushInterval)
      .size(size)
      .readWrite(readWrite)
      .properties(props)
      .build();
  // 添加奥configuration配置类的换集合中
  /*
  org.apache.ibatis.session.Configuration#addCache
  public void addCache(Cache cache) {
    caches.put(cache.getId(), cache);
  }
  */
  configuration.addCache(cache);
  // 赋值到当前缓存
  currentCache = cache;
  // 返回缓存
  return cache;
}
2.1.4:解析resultMap节点

源码如下;

org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElements
private void resultMapElements(List<XNode> list) throws Exception {
  // 循环处理所有的<resultMap/>节点
  for (XNode resultMapNode : list) {
    try {
      // <202107181410>
      resultMapElement(resultMapNode);
    } catch (IncompleteElementException e) {
      // ignore, it will be retried
    }
  }
}

<202107181410>处源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElement(org.apache.ibatis.parsing.XNode)
private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
  return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList());
}

private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
  ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
  // 获得id属性的值
  String id = resultMapNode.getStringAttribute("id",
      resultMapNode.getValueBasedIdentifier());
  // 获得type属性的值
  String type = resultMapNode.getStringAttribute("type",
      resultMapNode.getStringAttribute("ofType",
          resultMapNode.getStringAttribute("resultType",
              resultMapNode.getStringAttribute("javaType"))));
  // 获取extend的值,即继承的resultMap
  String extend = resultMapNode.getStringAttribute("extends");
  // 获取autoMapping的值,即resultMap中没有配置result的结果列是否自动映射
  Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
  // 创建resultMap中配置的type对应的类的class类型对象
  Class<?> typeClass = resolveClass(type);
  Discriminator discriminator = null;
  List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
  resultMappings.addAll(additionalResultMappings);
  List<XNode> resultChildren = resultMapNode.getChildren();
  // 获取resultMap的子节点并循环处理子节点
  for (XNode resultChild : resultChildren) {
    // <202107191613>
    // 如果是<constructor>标签
    if ("constructor".equals(resultChild.getName())) {
      processConstructorElement(resultChild, typeClass, resultMappings);
    } else if ("discriminator".equals(resultChild.getName())) { 
      // 解析<discriminator>标签,鉴别器,用于依赖某个列的值,来动态为属性赋值
      // <202107211750>
      discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
    } else { // 到这里,说明就是<id>,<result>,<collection>,<association>等标签
      ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
      // 如果是<id>标签,则增加ResultFlag.ID枚举到flags中
      if ("id".equals(resultChild.getName())) {
        flags.add(ResultFlag.ID);
      }
      // <202107211806>
      resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
    }
  }
  // 构造函数如下:
  /*
 public ResultMapResolver(MapperBuilderAssistant assistant, String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) {
    // 映射帮助类
    this.assistant = assistant;
    this.id = id;
    // 实体类型
    this.type = type;
    // 继承的resultMap信息
    this.extend = extend;
    // 鉴别器
    this.discriminator = discriminator;
    // 内部所有的节点信息
    this.resultMappings = resultMappings;
    // 是否自动映射没有配置的属性
    this.autoMapping = autoMapping;
  }
  */
  ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
  try {
    // <202107211821>
    return resultMapResolver.resolve();
  } catch (IncompleteElementException  e) {
    configuration.addIncompleteResultMap(resultMapResolver);
    throw e;
  }
}

<202107191613>处是解析resultMap的constructor子标签,源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#processConstructorElement
private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
  // 获取所有的子节点,如下可能配置:
  /*
	<resultMap id="myResultMap" type="yudaosourcecode.mybatis.model.TestResultmapConstructor">
	    <constructor>
	        <idArg javaType="integer" jdbcType="INTEGER" column="id"/>
	        <arg javaType="string" jdbcType="VARCHAR" column="myname"/>
	        <arg javaType="integer" jdbcType="INTEGER" column="myage"/>
	    </constructor>
	</resultMap>
  */
  List<XNode> argChildren = resultChild.getChildren();
  for (XNode argChild : argChildren) {
    // ResultFlag是一个枚举类,如下:
    /*
	org.apache.ibatis.mapping.ResultFlag
	public enum ResultFlag {
	  ID, CONSTRUCTOR
	}
	*/
    ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
    // 先添加ResultFlag.CONSTRUCTOR
    flags.add(ResultFlag.CONSTRUCTOR);
    // 如果时idArg,再添加ResultFlag.ID
    if ("idArg".equals(argChild.getName())) {
      flags.add(ResultFlag.ID);
    }
    // <202107191636>
    // 将当前子节点构造成ReusltMapping对象,并添加到resultMappings集合中
    resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
  }
}

<202107191636>buildResultMappingFromContext用于构建ResultMapping对象,具体参考2.2:构建ResultMapping<202107211750>处鉴别器,源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#processDiscriminatorElement
private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
  // 获取各种属性
  String column = context.getStringAttribute("column");
  String javaType = context.getStringAttribute("javaType");
  String jdbcType = context.getStringAttribute("jdbcType");
  String typeHandler = context.getStringAttribute("typeHandler");
  Class<?> javaTypeClass = resolveClass(javaType);
  @SuppressWarnings("unchecked")
  Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
  JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
  Map<String, String> discriminatorMap = new HashMap<String, String>();
  for (XNode caseChild : context.getChildren()) {
    // 获取case 的处理的对应column的value
    String value = caseChild.getStringAttribute("value");
    // 获取使用的resultMap
    String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings));
    discriminatorMap.put(value, resultMap);
  }
  // 构造Discriminator
  return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap);
}

<202107211806>处buildResultMappingFromContext是构建ResultMapping对象,具体参考2.2:构建ResultMapping<202107211821>处具体参考2.3:解析resultMap标签为ResultMap对象

2.1.5:解析sql标签

源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#sqlElement
// list参数是所有sql节点信息,因为在一个命名空间的的mapper中可以配置多个,所以是个集合
private void sqlElement(List<XNode> list) throws Exception {
  // 这里的datbaseid一般是在mybatis通过数据库的连接信息获取的
  // 另外在sql节点内也可以配置databaseId属性
  if (configuration.getDatabaseId() != null) {
    sqlElement(list, configuration.getDatabaseId());
  }
  // <202107301107>
  sqlElement(list, null);
}

<202107301107>处源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#sqlElement(java.util.List<org.apache.ibatis.parsing.XNode>, java.lang.String)
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
  // 循环所有节点,依次处理
  for (XNode context : list) {
    // 获取节点配置的databaseId属性的值
    String databaseId = context.getStringAttribute("databaseId");
    // 获取节点id属性的值
    String id = context.getStringAttribute("id");
    // 添加命名空间前缀,生成完整的id信息,因为不同命名空间的id是可以重复的,所以这里需要添加命名空间前缀
    id = builderAssistant.applyCurrentNamespace(id, false);
    // <202107301123>
    if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) sqlFragments.put(id, context);
  }
}

<202107301123>databaseIdMatchesCurrent源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#databaseIdMatchesCurrent
// 方法整体逻辑比较复杂,知道逻辑即可
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
  // 如果是全局的databseId有值
  if (requiredDatabaseId != null) {
    // 如果是和databseId属性配置的不相等,则return false
    if (!requiredDatabaseId.equals(databaseId)) {
      return false;
    }
  } else {
    // 如果是没有全局databaseId,但是设置了databseId属性,return false
    if (databaseId != null) {
      return false;
    }
    // 如果是已经包含该sql
    if (this.sqlFragments.containsKey(id)) {
      // 获取已经包含的sql
      XNode context = this.sqlFragments.get(id);
      // 从已经包含的sql节点信息获取配置的databaseId属性,如果配置了也返回false
      if (context.getStringAttribute("databaseId") != null) {
        return false;
      }
    }
  }
  // 最后直接返回true
  return true;
}

<202107301123>sqlFragments定义如下:

// key:带有命名空间的sql节点id,value:sql节点对应的节点对象
private Map<String, XNode> sqlFragments;
2.1.6:解析增删改查标签

源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>)
private void buildStatementFromContext(List<XNode> list) {
  // 如果有要求的databaseId
  if (configuration.getDatabaseId() != null) {
    buildStatementFromContext(list, configuration.getDatabaseId());
  }
  // <202107301506>
  // 构建表达式
  buildStatementFromContext(list, null);
}

<202107301506>处是构建statement,源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>, java.lang.String)
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  // 依次遍历<insert><delete><update><select>节点,执行解析
  for (XNode context : list) {
  	// <202107301526>
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
      // 如果是解析失败了,则添加到不完备的语句集合中,后续会再次尝试解析
      // protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
      configuration.addIncompleteStatement(statementParser);
    }
  }
}

<202107301526>处是创建XMLStatementBuilder并执行具体的解析,详细参考这篇文章

2.2:构建ResultMapping

源码如下:

org.apache.ibatis.builder.xml.XMLMapperBuilder#buildResultMappingFromContext
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, ArrayList<ResultFlag> flags) throws Exception {
  // 获取各种各样的属性
  String property = context.getStringAttribute("property");
  String column = context.getStringAttribute("column");
  String javaType = context.getStringAttribute("javaType");
  String jdbcType = context.getStringAttribute("jdbcType");
  String nestedSelect = context.getStringAttribute("select");
  String nestedResultMap = context.getStringAttribute("resultMap",
      processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
  String notNullColumn = context.getStringAttribute("notNullColumn");
  String columnPrefix = context.getStringAttribute("columnPrefix");
  String typeHandler = context.getStringAttribute("typeHandler");
  String resulSet = context.getStringAttribute("resultSet");
  String foreignColumn = context.getStringAttribute("foreignColumn");
  boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
  // 解析javaType为对应的class
  Class<?> javaTypeClass = resolveClass(javaType);
  // 解析typeHandler为对应的class
  @SuppressWarnings("unchecked")
  Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
  // 解析jdbcType为对应的org.apache.itabis.type.JdbcType枚举类
  JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
  // <202107191647>
  return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn, lazy);
}

<202107191647>处是通过映射构建帮助类构造ResultMapping,源码如下:

org.apache.ibatis.builder.MapperBuilderAssistant#buildResultMapping
public ResultMapping buildResultMapping(
      Class<?> resultType,
      String property,
      String column,
      Class<?> javaType,
      JdbcType jdbcType,
      String nestedSelect,
      String nestedResultMap,
      String notNullColumn,
      String columnPrefix,
      Class<? extends TypeHandler<?>> typeHandler,
      List<ResultFlag> flags,
      String resultSet,
      String foreignColumn, 
      boolean lazy) {
  // 解析java类型的class
  // <202107191656>
  Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType);
  // 解析对一个的类型处理器类(数据库<->pojo属性的处理类)
  // <202107191704>
  TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
  // 解析<constructor>标签中的column属性
  // <202107211419>
  List<ResultMapping> composites = parseCompositeColumnName(column);
  // 如果是配置column,则将column赋值为null
  if (composites.size() > 0) column = null;
  // configuration:配置对象
  // property:当前标签的property属性值
  // column:当前标签的column属性值
  // javaTypeClass:当前标签的javaType对应的class
  // 创建结果映射构造器对象
  ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property, column, javaTypeClass);
  // 设置各种属性
  builder.jdbcType(jdbcType);
  // 设置内嵌的查询id,即其他的statement的id,最终是生成带有命名空间的id
  // 解析select属性,即引用的其他或者是自己的命名空间的statement
  // 如<collection ... select="yudaosourcecode.mybatis.mapper.TestResultmapCollectionManyMapper.getById1" .../>
  // 我认为既然是引用其他的statement,那么不管是否和当前命名空间一致,写上全路径会非常清晰,代码如下:
  /*
  org.apache.ibatis.builder.MapperBuilderAssistant#applyCurrentNamespace
  public String applyCurrentNamespace(String base, boolean isReference) {
    if (base == null) return null;
    if (isReference) {
      // is it qualified with any namespace yet?
      if (base.contains(".")) return base;
    } else {
      // is it qualified with this namespace yet?
      if (base.startsWith(currentNamespace + ".")) return base;
      if (base.contains(".")) throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);
    }
    return currentNamespace + "." + base;
  }
  */
  builder.nestedQueryId(applyCurrentNamespace(nestedSelect, true));
  // 设置内嵌的resultMap id(带有命名空间)
  // 如:<collection .. resultMap="xxx.yyy.zzz.someOtherResultMapId" .../>
  builder.nestedResultMapId(applyCurrentNamespace(nestedResultMap, true));
  builder.resultSet(resultSet);
  builder.typeHandler(typeHandlerInstance);
  builder.flags(flags == null ? new ArrayList<ResultFlag>() : flags);
  builder.composites(composites);
  builder.notNullColumns(parseMultipleColumnNames(notNullColumn));
  builder.columnPrefix(columnPrefix);
  builder.foreignColumn(foreignColumn);
  builder.lazy(lazy);
  // 构造ResultMapping对象,并返回
  return builder.build();
}

<202107191656>处事解析对应的java类型,源码如下:

org.apache.ibatis.builder.MapperBuilderAssistant#resolveResultJavaType
private Class<?> resolveResultJavaType(Class<?> resultType, String property, Class<?> javaType) {
  // 如果是传入的javaType和property不为null
  if (javaType == null && property != null) {
    try {
      // 通过反射包工具类,获取property的class类型
      MetaClass metaResultType = MetaClass.forClass(resultType);
      javaType = metaResultType.getSetterType(property);
    } catch (Exception e) {
      //ignore, following null check statement will deal with the situation
    }
  }
  // 如果是到这javaType还为null,则赋值为Object.class
  if (javaType == null) {
    javaType = Object.class;
  }
  return javaType;
}

<202107191704>处是解析获取对应的类型处理器实例,源码如下:

org.apache.ibatis.builder.BaseBuilder#resolveTypeHandler
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
  // 为null,代表用户没有为该映射设置类型处理器,直接返回null
  if (typeHandlerType == null) return null;
  // 从类型处理器注册器中获取类型处理器
  TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
  if (handler == null) {
    // 如果是在类型处理器注册机中没有,则直接实例化出来一个
    handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
  }
  return handler;
}

<202107211419>处是解析<resultMap>中<collection>中column属性,源码如下:

org.apache.ibatis.builder.MapperBuilderAssistant#parseCompositeColumnName
private List<ResultMapping> parseCompositeColumnName(String columnName) {
  List<ResultMapping> composites = new ArrayList<ResultMapping>();
  // 关联查询配置多个映射,可能值"{oneid=id,outerManyName=manyname}"
  // 如果包含"=",并且包含","
  if (columnName != null && (columnName.indexOf('=') > -1 || columnName.indexOf(',') > -1)) {
  	// 分别解析处配置的属性映射关系
    StringTokenizer parser = new StringTokenizer(columnName, "{}=, ", false);
    while (parser.hasMoreTokens()) {
      String property = parser.nextToken();
      String column = parser.nextToken();
      ResultMapping.Builder complexBuilder = new ResultMapping.Builder(configuration, property, column, configuration.getTypeHandlerRegistry().getUnknownTypeHandler());
      // 创建结果映射对象并添加到要返回的集合中
      composites.add(complexBuilder.build());
    }
  }
  return composites;
}

2.3:解析resultMap标签为ResultMap对象

源码如下:

org.apache.ibatis.builder.ResultMapResolver#resolve
public ResultMap resolve() {
  return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
}

org.apache.ibatis.builder.MapperBuilderAssistant#addResultMap
public ResultMap addResultMap(
      String id,
      Class<?> type,
      String extend,
      Discriminator discriminator,
      List<ResultMapping> resultMappings,
      Boolean autoMapping) {
  // 添加mapper xml的namespace前缀
  id =applyCurrentNamespace(id, false);
  // 给extend添加mapper xml的namespace前缀
  extend = applyCurrentNamespace(extend, true);
  // ResultMap构造器
  ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping);
  // 有extend
  if (extend != null) {
    // 检查是否有该extend的resultMap,没有则异常
    if (!configuration.hasResultMap(extend)) {
      throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
    }
    // 从configuration获取extend对应的ResultMap
    ResultMap resultMap = configuration.getResultMap(extend);
    // 获取继承的ResultMap内的所有的ResultMappings
    List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
    // 删除继承的在当前已有的
    extendedResultMappings.removeAll(resultMappings);
    // 判断当前ResultMap是声明了<construcotr>
    boolean declaresConstructor = false;
    for (ResultMapping resultMapping : resultMappings) {
      // 声明了,则将标记设置为true
      if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
        declaresConstructor = true;
        break;
      }
    }
    // 包含<constructor>声明,循环继承的,删除里面的声明
    if (declaresConstructor) {
      Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
      while (extendedResultMappingsIter.hasNext()) {
        if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
          extendedResultMappingsIter.remove();
        }
      }
    }
    // 添加继承的ResultMapping信息到当前
    resultMappings.addAll(extendedResultMappings);
  }
  resultMapBuilder.discriminator(discriminator);
  // 构造ResultMap
  ResultMap resultMap = resultMapBuilder.build();
  // 添加<resultMap/>标签对应的ResultMap对象到configuration中
  configuration.addResultMap(resultMap);
  // 返回解析出的ResultMap对象
  return resultMap;
}

3:MapperProxyFactory

这是一个用来生成mapper接口jdk动态代理的工厂类,源码如下:

org.apache.ibatis.binding.MapperProxyFactory
public class MapperProxyFactory<T> {
  // mapper接口
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
  
  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
  // 获取代理的mapper接口
  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  // 创建mapper接口代理类
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    // 生成jdk动态代理中用于执行方法的java.lang.reflect.InvocationHandler类MapperProxy
    //, 该类是核心类,在执行数据库操作时会执行到这里的invoke方法
    // 2021-09-09 17:58:56
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

2021-09-09 17:58:56处是创建org.apache.ibatis.bingding.MapperProxy,这是生成mapper接口动态代理类要使用的java.lang.reflect.InvocationHandler实现类。我们重点来看其对invoke方法的实现,具体参考3.1:MaperProxy#invoke

3.1:MaperProxy#invoke

当我们执行如下代码:

String resource = "mybatis/mybatis-config-1.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSession = this.sqlSessionFactory.openSession();
this.testExtendAndAutoMappingMapper =
        this.sqlSession.getMapper(TestExtendAndAutoMappingMapper.class);

获取mapper接口的实现类时,返回的其实就是基于jdk的生成动态代理类,通过前面分析,我们知道其InvocationHandler使用的是MaperProxy,因此调用mapper方法时,会首先执行org.apache.ibatis.binding.MapperProxy#invoke,源码如下:

org.apache.ibatis.binding.MapperProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  // toString,equals,hashCode等Object类定义的方法
  if (Object.class.equals(method.getDeclaringClass())) {
    try {
      // 调用的是Object类的方法,直接调用并返回
      return method.invoke(this, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
  // 创建MapperMethod,该类封装接口信息,方法信息,以及全局配置文件的configuration对象信息
  // 源码如下:
  /*
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }
  */
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  // 通过MapperMethod对象执行
  // 2021-09-10 15:12:30
  return mapperMethod.execute(sqlSession, args);
}

2021-09-10 15:12:30处是通过org.apache.ibatis.bingding.MaperMethod对象执行,具体参考3.2::MapperMethod#execute

3.2::MapperMethod#execute

源码如下:

org.apache.ibatis.binding.MapperMethod#execute
public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  // 根据对应的statement类型,调用会话对象org.apache.ibatis.session.SqlSession的API执行
  // 具体的数据库操作
  // 2021-09-10 15:17:17
  if (SqlCommandType.INSERT == command.getType()) {
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.insert(command.getName(), param));
  } else if (SqlCommandType.UPDATE == command.getType()) {
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.update(command.getName(), param));
  } else if (SqlCommandType.DELETE == command.getType()) {
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.delete(command.getName(), param));
  } else if (SqlCommandType.SELECT == command.getType()) {
    if (method.returnsVoid() && method.hasResultHandler()) {
      executeWithResultHandler(sqlSession, args);
      result = null;
    } else if (method.returnsMany()) {
      result = executeForMany(sqlSession, args);
    } else if (method.returnsMap()) {
      result = executeForMap(sqlSession, args);
    } else {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = sqlSession.selectOne(command.getName(), param);
    }
  } else if (SqlCommandType.FLUSH == command.getType()) {
      result = sqlSession.flushStatements();
  } else {
    throw new BindingException("Unknown execution method for: " + command.getName());
  }
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    throw new BindingException("Mapper method '" + command.getName() 
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}

2021-09-10 15:17:17是根据用户命令的类型来调用会话对象org.apache.ibatis.session.SqlSession的不同方法,方法内部会调用执行器的子类org.apache.ibatis.executor.CachingExecutor来执行后续的操作,关于这个调用过程,可以参考这篇文章

写在后面

看源码真的是好无聊!!!不过,耐得住寂寞的人,才又有所成就吧!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值