mybatis

MyBatis源码系列--3.mybatis源码解析(上)

2019-04-30  本文已影响0人  WEIJAVA

分析源码,从编程式的 demo 入手

        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      
        SqlSession session = sqlSessionFactory.openSession(); 

        BlogMapper mapper = session.getMapper(BlogMapper.class);
         
        Blog blog = mapper.selectBlogById(1);

把文件读取成流的这一步我们就省略了。所以下面我们分成四步来分析

配置解析过程

配置解析的过程只解析了两种文件,一个是mybatis-config.xml 全局配置文件。另外就是可能有很多个的 Mapper.xml 文件,也包括在 Mapper 接口类上面定义的注解。
注:mybatis-config.xml 全局配置文件,包含了mapper映射文件,所以直接解析mybatis-config.xml 文件即可解析到所有mapper文件,
具体mybatis-config.xml 中的配置项参考mybatis中文文档:http://www.mybatis.org/mybatis-3/zh/index.html

image.png

现在开始代码跟进一步一步来分析:

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

跟进build方法

  public SqlSessionFactory build(InputStream inputStream) {
        return this.build((InputStream)inputStream, (String)null, (Properties)null);
  }

   public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            //这里面创建了一个 XMLConfigBuilder 对象(Configuration 对象也是这个时候创建的)
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
           ...
        return var5;
    }

   private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        //创建父类BaseBuilder 的Configuration 对象
        super(new Configuration());
        ...
    }

public abstract class BaseBuilder {
    //mybatis的所有配置信息都存储在这个对象中
    protected final Configuration configuration;

    public BaseBuilder(Configuration configuration) {
        this.configuration = configuration;
       ...
    }

XMLConfigBuilder 是抽象类 BaseBuilder 的一个子类,专门用来解析全局配置文件,针对不同的构建目标还有其他的一些子类,比如:
XMLMapperBuilder:解析 Mapper 映射器(解析BlogMapper.xml)
XMLStatementBuilder:解析增删改查标签(解析BlogMapper.xml里面的sql标签)
我们回到 var5 = this.build(parser.parse()); 这里,继续跟进

    public Configuration parse() {
        if (this.parsed) {//配置文件只能解析一次
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            //从mybatis-config.xml的configuration标签下开始解析
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration;
        }
    }

跟进parseConfiguration方法

//解析configuration标签下的所有一级标签
private void parseConfiguration(XNode root) {
        try { 
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfs(settings);
            this.loadCustomLogImpl(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

其中每一个配置含义,可以参考官网文档:http://www.mybatis.org/mybatis-3/zh/configuration.html#mappers

image.png
  private void propertiesElement(XNode context) throws Exception {
        if (context != null) {
            Properties defaults = context.getChildrenAsProperties();
            String resource = context.getStringAttribute("resource");
            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.");
            }

            if (resource != null) {
                defaults.putAll(Resources.getResourceAsProperties(resource));
            } else if (url != null) {
                defaults.putAll(Resources.getUrlAsProperties(url));
            }

            Properties vars = this.configuration.getVariables();
            if (vars != null) {
                defaults.putAll(vars);
            }

            this.parser.setVariables(defaults);
            this.configuration.setVariables(defaults);
        }

    }

上面的一系列解析,都会封装到Configuration 对象中

再回到 this.build(parser.parse());的build方法中

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


public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    public SqlSession openSession() {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    }
}

可以看出 new 一个DefaultSqlSessionFactory 对象直接返回

小结:
以上主要完成了 config 配置文件、Mapper 文件、Mapper 接口上的注解的解析,得到了一个最重要的对象 Configuration,这里面存放了全部的配置信息,它在属性里面还有各种各样的容器,最后,返回了一个 DefaultSqlSessionFactory,里面持有了 Configuration 的实例


SQLSessionFactory.build时序图.jpg

——学自咕泡学院

上一篇 下一篇

猜你喜欢

热点阅读