我们的调试环境:
调试代码:
public class MyaaTest {
public static void main(String[]args) {
String resource = "config.xml";
try {
//1.读取配置文件
InputStream in= Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(in); //解析配置文件
//2. 打开连接
SqlSession session=sessionFactory.openSession();
String statement="com.it.dao.UserMapper.selectById";
//3.方法调用
User user=session.selectOne(statement);
System.out.println(user.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
第一步:进入Resources 类中,这个类对于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); //【入】
if (in == null) { //五个classloader 都失败了,那么就只能报错了!
throw new IOException("Could not find resource " + resource);
}
return in;
}
第二步:进入ClassLoaderWrapper 很明显这是类加载器的包装类
看一下成员:
public class ClassLoaderWrapper {
ClassLoader defaultClassLoader;
ClassLoader systemClassLoader;
ClassLoaderWrapper() {
systemClassLoader = ClassLoader.getSystemClassLoader();
}
...
}
我们使用的是静态方法,这个里面有两个类加载器(我们之前在学习jvm的时候,学习过classLoader,它的主要功能就是负责在文件系统中,找到class文件,加载到jvm中),我们这里希望找到的是xml。
注意classLoader为null
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
return getResourceAsStream(resource, getClassLoaders(classLoader)); //注入了五个classloader
}
看返回的第二个参数:
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{
classLoader, //null
defaultClassLoader, //null
Thread.currentThread().getContextClassLoader(),
getClass().getClassLoader(),
systemClassLoader};
}
返回了五个类加载器,第一个,第二个是Null,默认的类加载器,没有发现实例化的过程。
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
for (ClassLoader cl : classLoader) { //遍历Classloader
if (null != cl) {
InputStream returnValue = cl.getResourceAsStream(resource); //尝试进行加载
if (null == returnValue) { //查找失败,再次尝试
returnValue = cl.getResourceAsStream("/" + resource);
}
if (null != returnValue) { return returnValue; } //加载成功就推出,返回流
}
}
return null;
}
我们的配置文件,就在根目录下,第三个类加载器 找到了我们的xml文件
第三步:进入SqlSessionFactoryBuilder 类中
该类的结构,分为两部分(还有一个列外)
一个小组分为三个build和一个dobuild(), 第一组传入的主要是Reader,第二组传入的是inputStream,我们自然走第二组
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
这些重载就是传入参数不同而已,但是为了扩展,又不得不这样做(就如同go里面可以返回多个参数一个道理)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
}
第四步:进入XMLConfigBuilder类中
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed; //是否已解析,XPath解析器,环境
private XPathParser parser;
private String environment;
}
该类的[构造函数]结构和SqlSessionFactoryBuilder一样,通用分为两组reader+stream,最后流入builder方法中进行执行
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
它使用stream,构建了XPathParser类,传入下一个终构造函数中
XPath解析器:使用的是jdk的包,封装了一下,使用起来更方便
public class XPathParser {
private Document document;
private boolean validation;
private EntityResolver entityResolver;
private Properties variables;
private XPath xpath;
}
终构造函数,将各个属性都配置好了,
//上面6个,封装出了parser,流入这里
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration()); ///首先调用父类初始化Configuration
ErrorContext.instance().resource("SQL Mapper Configuration"); //错误上下文设置成SQL Mapper Configuration(XML文件配置),以便后面出错了报错用吧
//将Properties全部设置到Configuration里面去
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
准备好了,开始解析:parse()
给一个待解析的模板xml
<?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 (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//解析配置
private void parseConfiguration(XNode root) {
try {
//分步骤解析
//issue #117 read properties first
//1.properties
propertiesElement(root.evalNode("properties"));
//2.类型别名
typeAliasesElement(root.evalNode("typeAliases"));
//3.插件
pluginElement(root.evalNode("plugins"));
//4.对象工厂
objectFactoryElement(root.evalNode("objectFactory"));
//5.对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//6.设置
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
//7.环境
environmentsElement(root.evalNode("environments"));
//8.databaseIdProvider
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//9.类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
//10.映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
查看这个类的父类:
public abstract class BaseBuilder {
//需要配置,类型别名注册,类型处理器注册3个东西
protected final Configuration configuration; //保存解析的结果
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
}
返回这个configuration结果就可以了。
将该结果进行封装,方便使用。
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
}
对于这个具体解析parse()内容很多,我们下一节再讲