mybatis源码总结--MapperBuilder(2)

首先在此声明,本人是通过学习湖畔微风《深入浅出mybatis》基础之上对代码进行总结。

《深入浅出mybatis》 http://blog.csdn.net/hupanfeng/article/details/9068003

项目中的sql语句、一级缓存等都会配置在Mapper文件中,在开发中要对其经常进行配置,所以对解析Mapper文件做一下详细的总结。

1、XMLConfigBuilder类中的mapperElement方法是解析Mapper文件的入口,在解析之前会检查子节点类型并获取子节点属性,加载配置文件获取字节流。然后创建XmlMapperBuilder对象,字节流作为参数传进去。

 private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url
, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            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.");
          }
        }
      }
    }
  }

1.1 在创建XMLMapperBuilder对象,其中一个参数为configuration.getSqlFragments(),返回Configuration类中的成员数性

 protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

StrictMap是Configuration类的一个内部类用来存储每个mapper元素下的sql标签,它继承了HashMap并重写了put和get方法

  @SuppressWarnings("unchecked")
    public V put(String key, V value) {
      if (containsKey(key))
        throw new IllegalArgumentException(name + " already contains value for " + key);
      if (key.contains(".")) {
        final String shortKey = getShortName(key);
        if (super.get(shortKey) == null) {
          super.put(shortKey, value);
        } else {
          super.put(shortKey, (V) new Ambiguity(shortKey));
        }
      }
      return super.put(key, value);
    }

通过代码可以看出,重写put方法重要是为了,当key值有重复时,抛出异常。

public V get(Object key) {
      V value = super.get(key);
      if (value == null) {
        throw new IllegalArgumentException(name + " does not contain value for " + key);
      }
      if (value instanceof Ambiguity) {
        throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
            + " (try using the full name including the namespace, or rename one of the entries)");
      }
      return value;
    }

get方法,获取不到值时,抛出异常。

1.2 XMLMapper的构造方法,其中MapperBuilderAssistant用于缓存、sql参数、查询返回的结果集处理。

  private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    super(configuration);
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
  }

2、XMLmapper中的parse方法

 public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }

2.1 configurationElement 方法解析mapper标签下的子节点。mybatis的一级缓存是默认开启的,可以通过追查buildStatemnetFromContext方法中调用XMLStatementBuilder类的parseStatementNode方法去验证而且想关闭一级缓存光配置useCache=false是没有用的,还必须配置flushCache=true才行。其它的方法有兴趣的朋友可以自己研究,这里就不做详细介绍了。

  private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new RuntimeException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

2.2 configuration.addLoadedResource(resource)添加到一个HashSet中表示此rescue的mapper文件已经被解析过了。

2.3 bindMapperForNameSpace方法,用于获取mapper的代理工厂对象,判断是否knowMappers中是否存在其代理工厂对象,knowMappers在上一节中有介绍。如果不存在,则创建其代理工程对象并保存在knowMappers中。

  private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {
          configuration.addLoadedResource("namespace:" + namespace);
          configuration.addMapper(boundType);
        }
      }
    }
  }

总结:(1)通过上一节和这节的总结,可以发现mybatis解析xml的套路。先获取配置文件的根节点,解析其子节点的类名称为(根节点名+MapperBulider)这样写的程序很有层次感。

(2)内部类并重写hashmap的get、put方法,对于内部类的学习http://blog.csdn.net/ilibaba/article/details/3866537。

(3)想要详细了解对Mapper文件的解析请学习XMLMapperBuilder类中的configurationElement方法。

				
时间: 2024-10-18 06:25:04

mybatis源码总结--MapperBuilder(2)的相关文章

Mybatis源码分析之Cache二级缓存原理 (五)

一:Cache类的介绍 讲解缓存之前我们需要先了解一下Cache接口以及实现MyBatis定义了一个org.apache.ibatis.cache.Cache接口作为其Cache提供者的SPI(ServiceProvider Interface) ,所有的MyBatis内部的Cache缓存,都应该实现这一接口 Cache的实现类中,Cache有不同的功能,每个功能独立,互不影响,则对于不同的Cache功能,这里使用了装饰者模式实现. 看下cache的实现类,如下图: 1.FIFOCache:先进

MyBatis源码分析(一)开篇

源码学习的好处不用多说,Mybatis源码量少.逻辑简单,将写个系列文章来学习. Mybatis的使用入口位于org.apache.ibatis.session包中的SqlSession,发现它是个接口,必然有个默认实现类org.apache.ibatis.session.defaults包中的DefaultSqlSession.我们从来没有new过这个类,按照Java惯例使用SqlSessionFactory里的工厂方法.发现它也是个接口,必然有默认实现类DefaultSqlSessionFa

mybatis源码分析(一)

mybatis源码分析(sqlSessionFactory生成过程) 1. mybatis框架在现在各个IT公司的使用不用多说,这几天看了mybatis的一些源码,赶紧做个笔记. 2. 看源码从一个demo引入如下: public class TestApp { private static SqlSessionFactory sqlSessionFactory; static { InputStream inputStream; String resource = "mybatis-confi

MyBatis源码解读(1)——SqlSessionFactory

在前面对MyBatis稍微有点了解过后,现在来对MyBatis的源码试着解读一下,并不是解析,暂时定为解读.所有对MyBatis解读均是基于MyBatis-3.4.1,官网中文文档:http://www.mybatis.org/mybatis-3/zh/getting-started.html,MyBatis-3.4.1.jar. 本应在开始读MyBatis源码时首先应该了解下MyBatis的SqlSession的四大对象:Executor.StatemenHandler.ParameterHa

MyBatis源码分析-MyBatis初始化流程

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程. MyBatis初始化的过程也就是创建Configura

MyBatis源码浅析

什么是MyBatis MyBatis是支持定制化SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手工设置参数以及抽取结果集.MyBatis 使用简单的 XML 或注解来配置和映射基本体,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录. MyBatis简单示例 虽然在使用MyBatis时一般都会使用XML文件,但是本文为了分析程序的简单性,简单的测试程序将不包含XML配置

MyBatis 源码分析——介绍

笔者第一次接触跟MyBatis框架是在2009年未的时候.不过那个时候的他并不叫MyBatis,而是叫IBatis.2010年的时候改为现在的名字--MyBatis.这几年过去了,对于笔者来讲有一点陌生了.而且那个时候他也没有这么出名.hibernate占了大部分市场.虽然笔者早年的时候查看过他的源码,但是并没有很深入去理解他.主要的原因是因为当时我还在看hibernate的源码.太累了所以就没有去认真的理解.现在笔者想要重新在来看一篇关于他的源码并加强对他的理解.也是对自己过程的一种回归吧.

MyBatis源码分析-IDEA新建MyBatis源码工程

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.项目GitHub地址 下载MyBatis源码,导入到IDEA工程(Maven工程)中,工程结构如下: 在pom.xml中新增如下依赖: <!-- MyS

深入浅出Mybatis系列(三)---配置详解之properties与environments(mybatis源码篇)[转]

上篇文章<深入浅出Mybatis系列(二)---配置简介(mybatis源码篇)>我们通过对mybatis源码的简单分析,可看出,在mybatis配置文件中,在configuration根节点下面,可配置properties.typeAliases.plugins.objectFactory.objectWrapperFactory.settings.environments.databaseIdProvider.typeHandlers.mappers这些节点.那么本次,就会先介绍prope