Appearance
上文中编写的测试代码如下,我们来逐行进行探究。
java
@Test
public void test1() throws IOException {
// 1. 通过类加载器对配置文件进行加载,加载成了字节输入流,存到内存中 注意:配置文件并没有被解析
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2. (1)解析了配置文件,封装configuration对象 (2)创建了DefaultSqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
// 3.问题:openSession()执行逻辑是什么?
// 3.(1)创建事务对象 (2)创建了执行器对象cachingExecutor (3)创建了DefaultSqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 委派给Executor来执行,Executor执行时又会调用很多其他组件(参数设置、解析sql的获取,sql的执行、结果集的封装)
User user = sqlSession.selectOne("com.itheima.mapper.UserMapper.findByCondition", 1);
System.out.println(user);
System.out.println("MyBatis源码环境搭建成功....");
sqlSession.close();
}
通过类加载器对配置文件进行加载
第一行是通过类加载对配置文件进行加载,返回输入流,此时配置文件并没有被解析;classLoaderWrapper本质上就是调用一个合适的类加载器,获取到resources目录的资源转换为输入流对象返回。
java
/**
* 读取resources目录下的文件,转换为输入流返回
* @param resource - 文件路径
* @param classLoader - 类加载器
*/
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
// classLoaderWrapper: ClassLoader 的包装器
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
}
return in;
}
我们来看classLoaderWrapper.getResourceAsStream()
这个方法,通过类加载器,从类路径获取资源;注意此时我们classLoader传递的是null,但是mybatis还有其他的默认类加载器,注意看getClassLoaders
方法;
java
// 通过类加载器,从类路径获取资源
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
// getClassLoaders
return getResourceAsStream(resource, getClassLoaders(classLoader));
}
//
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[] {
classLoader, // 参数指定的类加载器
defaultClassLoader, // 系统指定的默认加载器
Thread.currentThread().getContextClassLoader(), // 当前线程绑定的类加载器
getClass().getClassLoader(), // 当前类使用的类加载器
systemClassLoader
};
}
再来看getResourceAsStream()
方法,这是获取资源转换为输入流的主要逻辑:
- 通过给定的资源名和类加载器数组,循环遍历类加载器,从资源的路径中获取输入流。
- 首先尝试使用指定的类加载器读取资源,如果未找到,则再尝试使用默认的类加载器(上文代码显示了默认的类加载器)。
- 如果找到了资源,返回资源的输入流;如果未找到资源,返回null。
java
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
// 循环ClassLoader,通过指定或默认的ClassLoader读取文件
for (ClassLoader cl : classLoader) {
if (null != cl) {
InputStream returnValue = cl.getResourceAsStream(resource);
if (null == returnValue) {
// 如果没有获取到资源,在路径前+“/”再尝试获取一遍
returnValue = cl.getResourceAsStream("/" + resource);
}
if (null != returnValue) {
return returnValue;
}
}
}
return null;
}
小结
第一行代码就是使用类加载器获取配置文件的内容,转换为输入流的形式;类加载如果传递为null,会有默认的类加载器,当前线程的类加载器、当前类的类加载器。