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:
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
fromSqlSessionFactoryBuilder
Here we go.
Sqlsessionfactorybuilder class
org.apache.ibatis.session.SqlSessionFactoryBuilder
This class is full of various overloads of build methods.
//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:
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:
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:
<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:
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