How does mybatis parse configuration files?

Time:2021-4-8

Focus on “Java back end technology stack”

Reply to “interview” for full interview information

In previous articles, we combed the whole process of reading mybatis source code. Today, let’s talk about how mybatis parses configuration files in detail.

This is the flow chart of today’s analysis:

How does mybatis parse configuration files?

Let’s start with cases.

Demo case

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

Witness the miracle

fromSqlSessionFactoryBuilderHere we go.

Sqlsessionfactorybuilder class

org.apache.ibatis.session.SqlSessionFactoryBuilder

This class is full of various overloads of build methods.

How does mybatis parse configuration files?

//This method does nothing 
public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
 }

Finally, we come to another build method

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //Create an xmlconfigbuilder object 
      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 class

The constructor of this class is overloaded:

How does mybatis parse configuration files?

First enter:

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 ()); in parser.parse ();

mybatis- config.xml Where is it analyzed?

Look at this method:

//This method returns a configuration object
public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  //Key points
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

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

Finally, you can see that you are starting to parse the configuration file:

How does mybatis parse configuration files?

Enter the method 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);
    }
  }

This is mybatis- config.xml Content resolution, and then set to the configuration object.

So what we define Mapper.xml Where is it analyzed?

ours Mapper.xml In mybatis- config.xml The configuration in is as follows:

How does mybatis parse configuration files?

<mapper>There are four ways to use it

//1 use classpath
<mappers>
    <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
      <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
   <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
//2 use absolute URL path
<mappers>
   <mapper url="file:///var/mappers/AuthorMapper.xml"/>
   <mapper url="file:///var/mappers/BlogMapper.xml"/>
   <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
//3 using java class names
<mappers>
   <mapper class="org.mybatis.builder.AuthorMapper"/>
   <mapper class="org.mybatis.builder.BlogMapper"/>
   <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
//4 automatically scan all mappers under the package
<mappers>
   <package name="org.mybatis.builder"/>
</mappers>

To continue the source code analysis, let’s go to mybatis- config.xml In the analysis, we can see that:

How does mybatis parse configuration files?

Let’s go into this method

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        //Auto scan all mappers under package
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          //Release 
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          //Using java class names
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
             //According to the file storage directory, read XxxMapper.xml
            InputStream inputStream = Resources.getResourceAsStream(resource);
             //Mapper is more complex, call XML mapper builder
            //Note that in the for loop, each mapper renews an XML mapper builder to parse
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          //Use absolute URL path
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            //Mapper is more complex, call XML mapper builder
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          //Use classpath 
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            //Add this mapping directly to the configuration
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

Here’s what we just said above<mapper>The way of using as like as two peas is exactly the same.

Here, the configuration file mybatis- config.xml Define the mapping file with us XxxMapper.xml It’s all resolved.

Back to the sqlsessionfactorybuilder class

We talked about the parse method in xmlconfigbuilder and returned a configuration object.

build(parser.parse());

The build method is to pass in a configuration object and then build a defaultsqlsession object.

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

Go back to the line in our demo code:

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

This line of code is equivalent to:

SqlSessionFactory sqlSessionFactory = new new DefaultSqlSessionFactory();

By now, our whole process is finished.

Recommended reading

XML and annotation mapping in mybatis, so easy

Recommended Today

Third party calls wechat payment interface

Step one: preparation 1. Wechat payment interface can only be called if the developer qualification has been authenticated on wechat open platform, so the first thing is to authenticate. It’s very simple, but wechat will charge 300 yuan for audit 2. Set payment directory Login wechat payment merchant platform( pay.weixin.qq . com) — > Product […]