Mybatis源码

阅读更多

一.

SqlSessionFactoryBuilder源码片段:

SqlSessionFactoryBuilder.build()主要参数类型包括(Reader reader,String environment,Properties properties)/(InputStream inputStream,String environment, Properties properties)

public class SqlSessionFactoryBuilder {

 

  //Reader读取mybatis配置文件,传入构造方法

  //除了Reader外,其实还有对应的inputStream作为参数的构造方法,

  //这也体现了mybatis配置的灵活性

  public SqlSessionFactory build(Reader reader) {

    return build(reader, null, null);

  }

 

  public SqlSessionFactory build(Reader reader, String environment) {

    return build(reader, environment, null);

  }

  

  //mybatis配置文件 + properties, 此时mybatis配置文件中可以不配置properties,也能使用${}形式

  public SqlSessionFactory build(Reader reader, Properties properties) {

    return build(reader, null, properties);

  }

  

  //通过XMLConfigBuilder解析mybatis配置,然后创建SqlSessionFactory对象

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {

    try {

      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);

      //下面看看这个方法的源码

      return build(parser.parse());

    } catch (Exception e) {

      throw ExceptionFactory.wrapException("Error building SqlSession.", e);

    } finally {

      ErrorContext.instance().reset();

      try {

        reader.close();

      } catch (IOException e) {

        // Intentionally ignore. Prefer previous error.

      }

    }

  }

  

  

  //InputStream 读入

  public SqlSessionFactory build(InputStream inputStream) {

    return build(inputStream, null, null);

  }

 

  public SqlSessionFactory build(InputStream inputStream, String environment) {

    return build(inputStream, environment, null);

  }

 

  public SqlSessionFactory build(InputStream inputStream, Properties properties) {

    return build(inputStream, null, properties);

  }

 

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {

    try {

      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

      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.

      }

    }

  }

 

  public SqlSessionFactory build(Configuration config) {

    return new DefaultSqlSessionFactory(config);

  }

}

 

解析Mybatis主要用XMLConfigBuilder。

 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);

 parser.parse();

 

 XMLConfigBuilder的源码:

 public class XMLConfigBuilder extends BaseBuilder {

 

  //Reader读取mybatis配置文件

  public XMLConfigBuilder(Reader reader, String environment, Properties props) {

    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);

  }

  

  //InputStream读取mybatis配置文件

   public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {

    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);

  }

  

  //构造函数

  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {

    super(new Configuration());

    ErrorContext.instance().resource("SQL Mapper Configuration");

    this.configuration.setVariables(props);

    this.parsed = false;

    this.environment = environment;

    this.parser = parser;

  }

 

  // //外部调用此方法对mybatis配置文件进行解析

  public Configuration parse() {

    if (parsed) {

      throw new BuilderException("Each XMLConfigBuilder can only be used once.");

    }

    parsed = true;

////从根节点configuration

    parseConfiguration(parser.evalNode("/configuration"));

    return configuration;

  }

 

    //此方法就是依次解析configuration节点下的子节点

   //由此也可看出,我们在configuration下面能配置的节点为以下10个节点

  private void parseConfiguration(XNode root) {

    try {

      //issue #117 read properties first

      propertiesElement(root.evalNode("properties"));//read properties first

      typeAliasesElement(root.evalNode("typeAliases"));

      pluginElement(root.evalNode("plugins"));

      objectFactoryElement(root.evalNode("objectFactory"));

      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

      reflectionFactoryElement(root.evalNode("reflectionFactory"));

      settingsElement(root.evalNode("settings"));

      // read it after objectFactory and objectWrapperFactory issue #631

      environmentsElement(root.evalNode("environments"));

      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);

    }

  }

 }

 

 

PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-config.dtd">

 

//从根节点configuration

 

 

通过以上源码,我们就能看出,在mybatis的配置文件中:

1. configuration节点为根节点。

2. 在configuration节点之下,我们可以配置10个子节点, 分别为:properties、typeAliases、plugins、objectFactory、objectWrapperFactory、settings、environments、databaseIdProvider、typeHandlers、mappers。

 

 

二.properties节点和environments节点

 

 

 

 

 

 

 

   

 

 

 

 

 

 

 

 

 

 

那么,外部指定properties配置文件,直接配置为xmlp这两种方法都同时用了,那么哪种方法优先?当以上两种方法都用xml配置优先, 外部指定properties配置其次。

 

environments元素节点可以配置多个environment子节点, 怎么理解呢? 

假如我们系统的开发环境和正式环境所用的数据库不一样(这是肯定的), 那么可以设置两个environment,               两个id分别对应开发环境(dev)和正式环境(final),那么通过配置environments的default属性就能选择对应的environment了, 

将environments的deault属性的值配置为dev, 那么就会选择dev的environment。

 

XMLConfigBuilder对于properties和environments的解析:

public class XMLConfigBuilder extends BaseBuilder {

private boolean parsed;

//xml解析器

private XPathParser parser;

private String environment;

 

//上次说到这个方法是在解析mybatis配置文件中能配置的元素节点

//今天首先要看的就是properties节点和environments节点

private void parseConfiguration(XNode root) {

try {

 //优先解析properties元素 

 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"));

 //解析environments元素

 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);

}

}

 

 

//下面就看看解析properties的具体方法

private void propertiesElement(XNode context) throws Exception {

if (context != null) {

 //将子节点的 name 以及value属性set进properties对象

 //这儿可以注意一下顺序,xml配置优先, 外部指定properties配置其次

 Properties defaults = context.getChildrenAsProperties();

 //获取properties节点上 resource属性的值

 String resource = context.getStringAttribute("resource");

 //获取properties节点上 url属性的值, resource和url不能同时配置

 String url = context.getStringAttribute("url");

 if (resource != null && url != null) {

throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");

 }

 //把解析出的properties文件set进Properties对象

 if (resource != null) {

defaults.putAll(Resources.getResourceAsProperties(resource));

 } else if (url != null) {

defaults.putAll(Resources.getUrlAsProperties(url));

 }

 //将configuration对象中已配置的Properties属性与刚刚解析的融合

 //configuration这个对象会装载所解析mybatis配置文件的所有节点元素,以后也会频频提到这个对象

 //既然configuration对象用有一系列的get/set方法, 那是否就标志着我们可以使用java代码直接配置? 

 //答案是肯定的, 不过使用配置文件进行配置,优势不言而喻

 Properties vars = configuration.getVariables();

 if (vars != null) {

defaults.putAll(vars);

 }

 //把装有解析配置propertis对象set进解析器, 因为后面可能会用到

 parser.setVariables(defaults);

 //set进configuration对象

 configuration.setVariables(defaults);

}

}

 

//下面再看看解析enviroments元素节点的方法

private void environmentsElement(XNode context) throws Exception {

if (context != null) {

if (environment == null) {

//解析environments节点的default属性的值

//例如:

environment = context.getStringAttribute("default");

}

//递归解析environments子节点

for (XNode child : context.getChildren()) {

//, 只有enviroment节点有id属性,那么这个属性有何作用?

//environments 节点下可以拥有多个 environment子节点

//类似于这样: ......

//意思就是我们可以对应多个环境,比如开发环境,测试环境等, 由environments的default属性去选择对应的enviroment

String id = child.getStringAttribute("id");

//isSpecial就是根据由environments的default属性去选择对应的enviroment

if (isSpecifiedEnvironment(id)) {

//事务, mybatis有两种:JDBC 和 MANAGED, 配置为JDBC则直接使用JDBC的事务,配置为MANAGED则是将事务托管给容器, 

TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));

//enviroment节点下面就是dataSource节点了,解析dataSource节点(下面会贴出解析dataSource的具体方法)

DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));

DataSource dataSource = dsFactory.getDataSource();

Environment.Builder environmentBuilder = new Environment.Builder(id)

 .transactionFactory(txFactory)

 .dataSource(dataSource);

//老规矩,会将dataSource设置进configuration对象

configuration.setEnvironment(environmentBuilder.build());

}

}

}

}

 

//下面看看dataSource的解析方法

private DataSourceFactory dataSourceElement(XNode context) throws Exception {

if (context != null) {

//dataSource的连接池

String type = context.getStringAttribute("type");

//子节点 name, value属性set进一个properties对象

Properties props = context.getChildrenAsProperties();

//创建dataSourceFactory

DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();

factory.setProperties(props);

return factory;

}

throw new BuilderException("Environment declaration requires a DataSourceFactory.");

}

 

在配置dataSource的时候使用了 ${driver} 这种表达式, 这种形式是怎么解析的?其实,是通过PropertyParser这个类解析:

/**

 * 这个类解析${}这种形式的表达式

 */

public class PropertyParser {

 

  public static String parse(String string, Properties variables) {

    VariableTokenHandler handler = new VariableTokenHandler(variables);

    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);

    return parser.parse(string);

  }

 

  private static class VariableTokenHandler implements TokenHandler {

    private Properties variables;

 

    public VariableTokenHandler(Properties variables) {

      this.variables = variables;

    }

 

    public String handleToken(String content) {

      if (variables != null && variables.containsKey(content)) {

        return variables.getProperty(content);

      }

      return "${" + content + "}";

    }

  }

}

你可能感兴趣的:(Mybatis源码)