Our debugging environment:
Debug code:
public class MyaaTest { public static void main(String[]args) { String resource = "config.xml"; try { //1. Read configuration file InputStream in= Resources.getResourceAsStream(resource); SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(in); //Resolve profile //2. Open connection SqlSession session=sessionFactory.openSession(); String statement="com.it.dao.UserMapper.selectById"; //3. Method call User user=session.selectOne(statement); System.out.println(user.toString()); } catch (IOException e) { e.printStackTrace(); } } }
Step 1: enter the Resources class, which is familiar with xml
public static InputStream getResourceAsStream(String resource) throws IOException { return getResourceAsStream(null, resource); } public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException { InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); //[in] if (in == null) { //If all five classloader s fail, you can only report an error! throw new IOException("Could not find resource " + resource); } return in; }
Step 2: enter ClassLoaderWrapper, which is obviously the wrapper class of classloader
Look at the members:
public class ClassLoaderWrapper { ClassLoader defaultClassLoader; ClassLoader systemClassLoader; ClassLoaderWrapper() { systemClassLoader = ClassLoader.getSystemClassLoader(); } ... }
We use the static method. There are two classloaders in this method (when we studied the jvm, we learned classLoader. Its main function is to find the class file in the file system and load it into the jvm). What we want to find here is xml.
Note that classLoader is null
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) { return getResourceAsStream(resource, getClassLoaders(classLoader)); //Five classloader s are injected }
Look at the second parameter returned:
ClassLoader[] getClassLoaders(ClassLoader classLoader) { return new ClassLoader[]{ classLoader, //null defaultClassLoader, //null Thread.currentThread().getContextClassLoader(), getClass().getClassLoader(), systemClassLoader}; }
Five class loaders are returned. The first one and the second one are Null. The default class loader does not find the instantiation process.
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { for (ClassLoader cl : classLoader) { //Traverse Classloader if (null != cl) { InputStream returnValue = cl.getResourceAsStream(resource); //Try to load if (null == returnValue) { //Find failed, try again returnValue = cl.getResourceAsStream("/" + resource); } if (null != returnValue) { return returnValue; } //Launch after loading successfully, return to stream } } return null; }
Our configuration file, in the root directory, the third classloader finds our xml file
Step 3: enter SqlSessionFactoryBuilder class
The structure of this class is divided into two parts (there is also a column outside)
A group is divided into three builds and a dobuild(). The first group mainly introduces readers, and the second group introduces inputStream. Let's go to the second group naturally
public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); }
These overloads are just the difference of the incoming parameters, but in order to expand, they have to do so (just like the principle that multiple parameters can be returned in go)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); }
Step 4: enter XMLConfigBuilder class
public class XMLConfigBuilder extends BaseBuilder { private boolean parsed; //Resolved, XPath parser, environment private XPathParser parser; private String environment; }
The [constructor] structure of this class is the same as SqlSessionFactoryBuilder, which is generally divided into two groups of readers + streams, and finally flows into the builder method for execution
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); }
It uses stream to build the XPathParser class, which is passed into the next final constructor
XPath parser: the package of jdk is used. It's more convenient to use
public class XPathParser { private Document document; private boolean validation; private EntityResolver entityResolver; private Properties variables; private XPath xpath; }
The final constructor, which configures all the properties,
//The above six encapsulate the parser and flow into here private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ///First call the parent class to initialize Configuration ErrorContext.instance().resource("SQL Mapper Configuration"); //The error context is set to SQL Mapper Configuration(XML file configuration), so that an error is reported later //Set all Properties to Configuration this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; }
Ready to start parsing: parse()
Give a template xml to be parsed
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
public Configuration parse() { //If it has been parsed, an error will be reported if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; }
//Resolution configuration private void parseConfiguration(XNode root) { try { //Step by step analysis //issue #117 read properties first //1.properties propertiesElement(root.evalNode("properties")); //2. Type alias typeAliasesElement(root.evalNode("typeAliases")); //3. Plug in pluginElement(root.evalNode("plugins")); //4. Target factory objectFactoryElement(root.evalNode("objectFactory")); //5. Object packaging factory objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //6. Setting settingsElement(root.evalNode("settings")); // read it after objectFactory and objectWrapperFactory issue #631 //7. Environment environmentsElement(root.evalNode("environments")); //8.databaseIdProvider databaseIdProviderElement(root.evalNode("databaseIdProvider")); //9. Type processor typeHandlerElement(root.evalNode("typeHandlers")); //10. Mapper mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
View the parent class of this class:
public abstract class BaseBuilder { //Three things need to be configured: type alias registration and type processor registration protected final Configuration configuration; //Save the result of parsing protected final TypeAliasRegistry typeAliasRegistry; protected final TypeHandlerRegistry typeHandlerRegistry; }
Return the configuration result.
The result is encapsulated and easy to use.
public class DefaultSqlSessionFactory implements SqlSessionFactory { private final Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; } }
There are many contents about parse(), we will talk about them in the next section