zoukankan      html  css  js  c++  java
  • Mybatis是如何解析配置文件的?

    在以前文章中,我们把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中的配置是这样的:

    使用方式有以下四种:

    //1使用类路径





    //2使用绝对url路径





    //3使用java类名




    //4自动扫描包下所有映射器



    继续源码分析,我们在上面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.");
    }
    }
    }
    }
    }
    这里刚刚和我们的上面说的使用的方式完全是一模一样的。

    到这里,配置文件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();
    到这里,我们的整个流程就搞定了。

  • 相关阅读:
    反射式光电开关QRE1113
    labview程序性能优化
    labview中小黑点,小红点
    简述时钟周期、机器周期、指令周期的概念及三者之间的关系
    C++中的#和##运算符
    NTC与PTC压敏电阻在电源电路中起的作用
    常用DC-DC;AC-DC电源芯片
    PC817与TL431的配合电路探讨
    React入门
    WebRTC网关服务器单端口方案实现
  • 原文地址:https://www.cnblogs.com/tianweichang/p/14137948.html
Copyright © 2011-2022 走看看