关于java:Mybatis是如何解析配置文件的

7次阅读

共计 7065 个字符,预计需要花费 18 分钟才能阅读完成。

关注“Java 后端技术全栈”

回复“面试”获取全套面试材料

在以前文章中,咱们把 Mybatis 源码浏览的整个流程梳理了一遍。明天,咱们来具体聊聊,Mybatis 是如何解析配置文件的。

这是明天剖析的流程图:

还是从案例开始。

demo 案例

 public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            System.out.println(userMapper.selectById(1));
        } catch (Exception e) {e.printStackTrace();
        } finally {
            try {inputStream.close();
            } catch (IOException e) {e.printStackTrace();
            }
            sqlSession.close();}
    }

见证奇观

SqlSessionFactoryBuilder 开始。

SqlSessionFactoryBuilder 类

org.apache.ibatis.session.SqlSessionFactoryBuilder

该类里全是 build 办法各种重载。

// 这个办法啥也没干 
public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);
 }

最终来到另外一个 build 办法里:

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // 创立一个 XMLConfigBuilder 对象 
      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.}
    }
  }

XMLConfigBuilder 类

该类的构造方法重载:

首先进入:

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

build(parser.parse()); 中的 parser.parse();

mybatis-config.xml 在哪里解析的呢?

请看上面这个办法:

// 该办法返回一个 Configuration 对象
public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  // 关键点
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

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

终于看到开始解析配置文件了:

进入办法 parseConfiguration。

 private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(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);
    }
  }

这里就是把 mybatis-config.xml 内容解析,而后设置到 Configuration 对象中。

那么咱们定义的 Mapper.xml 是在哪里解析的呢?

咱们的 Mapper.xml 在 mybatis-config.xml 中的配置是这样的:

<mapper>应用形式有以下四种:

// 1 应用类门路
<mappers>
    <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
      <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
   <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
// 2 应用相对 url 门路
<mappers>
   <mapper url="file:///var/mappers/AuthorMapper.xml"/>
   <mapper url="file:///var/mappers/BlogMapper.xml"/>
   <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
// 3 应用 java 类名
<mappers>
   <mapper class="org.mybatis.builder.AuthorMapper"/>
   <mapper class="org.mybatis.builder.BlogMapper"/>
   <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
// 4 主动扫描包下所有映射器
<mappers>
   <package name="org.mybatis.builder"/>
</mappers>

持续源码剖析,咱们在下面 mybatis-config.xml 解析中能够看到:

咱们无妨进入这个办法看看:

 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");
          // 应用 java 类名
          if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);
             // 依据文件寄存目录,读取 XxxMapper.xml
            InputStream inputStream = Resources.getResourceAsStream(resource);
             // 映射器比较复杂,调用 XMLMapperBuilder
            // 留神在 for 循环里每个 mapper 都从新 new 一个 XMLMapperBuilder,来解析
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          // 应用相对 url 门路
          } else if (resource == null && url != null && mapperClass == null) {ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            // 映射器比较复杂,调用 XMLMapperBuilder
            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.");
          }
        }
      }
    }
  }

这里刚刚和咱们的下面说的 <mapper> 应用的形式齐全是截然不同的。

到这里,配置文件 mybatis-config.xml 和咱们定义映射文件 XxxMapper.xml 就全副解析实现。

回到 SqlSessionFactoryBuilder 类

后面讲到了 XMLConfigBuilder 中的 parse 办法,并返回了一个 Configuration 对象。

build(parser.parse());

这个 build 办法就是传入一个 Configuration 对象,而后构建一个 DefaultSqlSession 对象。

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

持续回到咱们的 demo 代码中这一行代码里:

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

这一行代码就相当于:

SqlSessionFactory sqlSessionFactory = new new DefaultSqlSessionFactory();

到这里,咱们的整个流程就搞定了。

举荐浏览

Mybatis 中 xml 和注解映射,so easy 啦

正文完
 0