首页 > 代码库 > MyBatis 源码分析——配置信息

MyBatis 源码分析——配置信息

MyBatis框架的启动前期需要加载相关的XML配置信息。从官网上我们可以了解到他具有十几个节点。其中笔者认为比较重要的节点是settings节点、properties节点、environments节点、 transactionManager节点。至于相关的作用到官网看过一篇都会明白。

MyBatis框架的XML配置到底有几个节点,这个必须去查看一下相关的DTD文件才行。DTA文件就在包名为org.apache.ibatis.builder.xml的下面。由于显示的问题笔者只复制出部分来。

<?xml version="1.0" encoding="UTF-8" ?>

<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>

<!ELEMENT databaseIdProvider (property*)>
<!ATTLIST databaseIdProvider
type CDATA #REQUIRED
>

从上面我们就可以看到根节点就是configuration。同时我们也能找到上面笔者提到的比较重要的几个节点。

1.properties 节点:可以把它理解为变量吧。事实上他可以是一个properties文件。
2.settings 节点:MyBatis框架有一些内部需要设置的配置信息。请查看官网对于settings的介绍。
3.typeAliases 节点:设置相应的别名。这样子可以简少重复的编写。
4.typeHandlers 节点:官网叫为类型处理器,大部用于结果集应射到对象的时候需要。
5.environments 节点:这个节点按道理来讲是大家最清楚的。因为设置数据连接的信息就在这里。
6.mappers 节点:对象模型与数据表映射的信息。
注意:笔者这里只是写出可能常用的。

知道了上面的信息之后,在去看对应的源码你就会发现很简单——大部分都是一一对应的。 来,让我们从上一章中讲到的切入口类(SqlSessionFactoryBuilder)的build方法查看进去。SqlSessionFactoryBuilder类里面重载了很多build的方法。但是所有的方法最后都会流入下面的方法中去。

 1 public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
 2     try {
 3       XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
 4       return build(parser.parse());
 5     } catch (Exception e) {
 6       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
 7     } finally {
 8       ErrorContext.instance().reset();
 9       try {
10         reader.close();
11       } catch (IOException e) {
12         // Intentionally ignore. Prefer previous error.
13       }
14     }
15   }

从上面的代码中我们可以看到这个方法有三个参数:

1.Reader reader:XML配置所在的文件流。

2.String environment:指定要执行的环境ID。

3.Properties properties:加载的变理信息。

XMLConfigBuilder类的作用就是用于加载XML配置文件,也可以理解为读取。他的parse方法充分的体现出他的作用来。

 1  public Configuration parse() {
 2     if (parsed) {
 3       throw new BuilderException("Each XMLConfigBuilder can only be used once.");
 4     }
 5     parsed = true;
 6     parseConfiguration(parser.evalNode("/configuration"));
 7     return configuration;
 8   }
 9 
10   private void parseConfiguration(XNode root) {
11     try {
12       Properties settings = settingsAsPropertiess(root.evalNode("settings"));
13       //issue #117 read properties first
14       propertiesElement(root.evalNode("properties"));
15       loadCustomVfs(settings);
16       typeAliasesElement(root.evalNode("typeAliases"));
17       pluginElement(root.evalNode("plugins"));
18       objectFactoryElement(root.evalNode("objectFactory"));
19       objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
20       reflectorFactoryElement(root.evalNode("reflectorFactory"));
21       settingsElement(settings);
22       // read it after objectFactory and objectWrapperFactory issue #631
23       environmentsElement(root.evalNode("environments"));
24       databaseIdProviderElement(root.evalNode("databaseIdProvider"));
25       typeHandlerElement(root.evalNode("typeHandlers"));
26       mapperElement(root.evalNode("mappers"));
27     } catch (Exception e) {
28       throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
29     }
30   }

从要上面的俩个方法中我们可以看到很眼熟的代码字眼。相信不难看出就是加载各个节点上的信息。事实上如果我们查看一下parse方法的返回类型我们就可以知道这个步骤最后就是为了生成相应的Configuration类实例。可以这样子讲有关配置信息加载最后一定都在Configuration类实例里面。所以查看Configuration类就变得很有意义。源码笔者就不贴出来占地方。

XMLConfigBuilder类源码里面有俩个地方是笔者比较喜欢的。一个是简单封装了读取XML的操作类——XPathParser类。可能对于JAVA来讲这不算什么。笔者从事过C#开发,所以感觉比较深吧。他只是做了一个简单的封装。可是笔者看起来在读取XML便得更加载顺心了——可读性更强了。

 public String evalString(Object root, String expression) {
    String result = (String) evaluate(expression, root, XPathConstants.STRING);
    result = PropertyParser.parse(result, variables);
    return result;
  }

  public Boolean evalBoolean(String expression) {
    return evalBoolean(document, expression);
  }

  public Boolean evalBoolean(Object root, String expression) {
    return (Boolean) evaluate(expression, root, XPathConstants.BOOLEAN);
  }

  public Short evalShort(String expression) {
    return evalShort(document, expression);
  }

上面是XPathParser类读取XML文件值各自需要调用的方法。我们可以清楚的可以知道返回类型和调要的节点。笔者认为简单一小步,可是明理一大步。XMLConfigBuilder类源码里面还有一个笔者认为需要去注意学习的地方——MetaClass类。他的功能跟反射有关系,同时用到ReflectorFactory类和Reflector类。他这样子封装之后用到反射功能便得更加的容易理解和可观。过多的介绍还不如自己亲自去看一下体验一下。

好了,现在让我们回过头在来看看build方法最后返回什么才是最重要的。从源码中我们知道他返回的是一个DefaultSqlSessionFactory类型。

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

本章的重点类出来了。当程序执行到上面的方法的时候,说明加载配置信息的工作已经结束了。一个新的工作又要开始了。让我们在看一下笔者的例子代码吧。

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
IProductMapper dao = session.getMapper(IProductMapper.class);

笔者调用的没有参数的openSession方法。从这个方法里我们可以看到他又去调用了一个叫openSessionFromDataSource的方法。这个方法才是结果和开始的连接点。

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

上面的方法中有几个比较重要的类:Executor类、DefaultSqlSession类。其中特别是Executor类。本章就不明讲。后面会讲到。我们可以看到最后返回类是DefaultSqlSession类。对于数据库的增删改查就依赖这个类。

显然文章到了这里就已经把接下来要去了解工作做了一个明确的方法——了解Executor类的功能。这样子我们就可以进一步的了解框架了。

MyBatis 源码分析——配置信息