Mybatis源码解析之初始化配置文件封装为Configuration源码详解

      接着上文太长的那个文章开始分析http://blog.csdn.net/ccityzh/article/details/71517490

     其实初始化的部分没有什么可以分析的,就是解析Xml文件,不会解析的可以查一下,现在常用的都是JDOM,DOM4J,不过这里不是用的这两种。分析的过程中有初始化某些关键的部分会单独拿出来分析一下。

      注:本文都是根据上一篇中实例为入口的,看到的非mybatis框架代码都是上节例子中的代码

     .首先根据配置文件获取SqlSessionFactory,这个过程也是mybatis初始化的过程,读取配置文件,封装配置文件的过程。

         sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

       根据类及方法的名字就可以看出使用构建模式构建sqlSessionFactory这个对象,一般复杂的对象都是用构建模式来创建对象。

(1)SqlSessionFactoryBuilder.java

 

//首先将配置文件以输入流的方式载入,接着使用一个重载的方法跳转到公共的build方法  
public SqlSessionFactory build(InputStream inputStream) {  
  return build(inputStream, null, null);  
}  
//使用XMLConfigBulder来封装输入流,最终会将输入流转化成一个解析XML大家所熟悉的Document对象  
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {  
  try {  
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);          (1)
    return build(parser.parse());  
  } catch (Exception e) {  
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);  
  } finally {  
    ErrorContext.instance().reset();  
    try {  
      inputStream.close();  
    } catch (IOException e) {  
      // Intentionally ignore. Prefer previous error.  
    }  
  }  
}  

       没什么难以理解的地方,关键代码也就那try中的两句,其他都是安全措施,下面进入到XMLConfigBuilder类中。

(2)XMLConfigBuilder.java

 

//没有什么直接利用另一个构造函数,转移到了XPathParser中了,可以了解一下JDK的XPath,反正我是不会
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }
//这个类中看到了我们解析XML过程中亲切的document对象了
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(inputStream));
  }

 (3)  XMLConfigBuilder.java      

         接着会看到更有令人欣喜的东西,慢慢的就会走到咱们mybatis_config文件中各个节点的解析,让你看到熟悉的内容,不要被这些没听过的东西吓到,只是一个层层封

装与转化的过程。下面转化为了document之后,也就这个转化的流程就结束了,接着就返回到了上面(1)出所标记的地方。目前的情况是:XMLConfigBuilder 中包含着XPathParser,XPathParser中包含着document对象。包含着document对象的parser对象在(1)处调用parse方法 。 

//惊不惊喜,这个类里面包含着配置文件中的根节点/configuration,同时也看到了我们最终将配置文件封装在的Configuration类中
public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
//这个就是解析配置文件的核心方法了,每个节点都拿出来单独解析,其中有很多框架运行起来的初始化操作,例如sql语句的封装了,拦截器的初始化了等
//理解这个方法还是比较关键的了
private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

 

   (4)  SqlSessionFactoryBuilder.java    

         先把整个流程看完,然后再单独取几个上面关键的节点解析分析一下。接着将各个配置节点解析到了Configuration类,然后(1)处调用 build(parser.parse())方法,也就是构建模式的第二步,利用Configuration构建出复杂的SqlSessionFactory对象,第一步是封装为Configuration的过程。

 

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

          这样就取到了打开Mybatis大门的钥匙SqlSessionFactory,DefaultSqlSessionFactory是SqlSessionFactory的默认实现类用它就可以随便创建出Mybatis的session,session也就相当于jdbc的connection,拿到session以后就是执行sql的过程了。

(5)下面大体上说下配置文件各个节点的作用,主要分析一下mapper这个节点,这个也是文中最关键的;plugin这个节点,以后会单独写一篇插件(拦截器)的执行流程,毕竟大多数时候分页都是直接使用Mybatis的插件的,这里就不说了;然后typeAlias节点可以看下,别名有时候会用到,也可以看到mybatis中已经有的jdk某些类的默认别名;其他节点感兴趣的可以自己看下。下面按源代码中的解析顺序大体分析一下。

           1.properties   这个到处都在用,配置各种参数,没什么可说的,例如mybatis_config中的    (略有不同)

           2.typeAliases,别名,这个就是有时候对象分布在各个包中,全路径太长,所以利用全路径注册一个别名,以后mybatis就认识这个别名了,在配置文件中使用起来就方便多了。我的实例中只有一个包,没什么可注册的,直到这个意思就好了,下面看下mybatis中已经注册的别名。

 

public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);

    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);

    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);

    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);

    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);

    registerAlias("ResultSet", ResultSet.class);
  }

        没什么可分析的,以后在mapper配置文件中可以方便的用,比如