当前位置: 首页> 教育> 就业 > MyBatis 源码学习 | Day 2 | MyBatis 初始化

MyBatis 源码学习 | Day 2 | MyBatis 初始化

时间:2025/7/18 16:53:42来源:https://blog.csdn.net/weixin_59271089/article/details/140882927 浏览次数:0次

运行阶段划分

根据上一篇MyBatis 源码学习 | Day 1 | 了解 MyBatis中最后使用 MyBatis 操作数据库的代码,我们可以把程序整体运行的流程划分为两个部分:

  1. MyBatis 初始化
  2. 数据读写阶段
/*** 使用 MyBatis 操作数据库** @author nx-xn2002* @date 2024-08-02*/
public class QueryWithMyBatis {public static void main(String[] args) throws IOException {//第一阶段:MyBatis初始化String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//第二阶段:数据读写阶段SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users = mapper.listAll();users.forEach(System.out::println);User user = mapper.selectUserById(1L);System.out.println(user);sqlSession.close();}
}

今天我们来探究第一阶段:MyBatis的初始化

MyBatis 初始化阶段

在 MyBatis 的初始化阶段,主要包含配置文件的解析和数据库连接等工作,可以看到,依次是调用了 Resource 类下的 getResourceAsStream 方法来把配置文件解析成 Stream 类对象,然后通过 SqlSessionFactoryBuilder 类下的 bulid 方法获取到 SqlSessionFactory 对象来管理数据库连接。我们深入到这两个方法中去

解析配置文件为输入流

Resource 类下的 getResourceAsStream 方法,试图获取到一个 InputStream 类的对象,核心方法的源代码如下:

/*** Returns a resource on the classpath as a Stream object** @param loader   The classloader used to fetch the resource* @param resource The resource to find* @return The resource* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);if (in == null) {throw new IOException("Could not find resource " + resource);}return in;
}

可以看到,这里是调用了 classLoaderWrapper 对象的同名方法来获取返回值,这个对象是 Resource 类的静态变量,它是在 Resource 类被加载时初始化的,类的加载过程包括:加载 -> 验证 -> 准备 -> 解析 -> 初始化,而 classLoaderWrapper 对象的赋值,就发生在初始化阶段
继续向下定位,可以看到 classLoaderWrapper 对象的这个方法,实际上是调用了另一个重载方法,方法的参数中 ClassLoader 对象变成了 ClassLoader 数组

/*** Get a resource from the classpath, starting with a specific class loader** @param resource    - the resource to find* @param classLoader - the first class loader to try* @return the stream or null*/public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {return getResourceAsStream(resource, getClassLoaders(classLoader));}
/*** Try to get a resource from a group of classloaders** @param resource    - the resource to get* @param classLoader - the classloaders to examine* @return the resource or null*/
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {for (ClassLoader cl : classLoader) {if (null != cl) {// try to find the resource as passedInputStream returnValue = cl.getResourceAsStream(resource);// now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resourceif (null == returnValue) {returnValue = cl.getResourceAsStream("/" + resource);}if (null != returnValue) {return returnValue;}}}return null;
}

我们可以从 getClassLoaders 方法中,知道传入的 ClassLoader 数组的大致内容

ClassLoader[] getClassLoaders(ClassLoader classLoader) {return new ClassLoader[]{classLoader,defaultClassLoader,Thread.currentThread().getContextClassLoader(),getClass().getClassLoader(),systemClassLoader};
}

结合上下文可以知道,数组中 classLoader 就是我们可以选择传入的自定义 ClassLoaderdefaultClassLoader 也是自定义的 ClassLoader 可以在 Resource 类中通过 set 方法传入,这两个默认值都是 nullThread.currentThread().getContextClassLoader() 这个方法返回当前线程的上下文类加载器,上下文类加载器可以被设置为任何类加载器,但默认情况下,它通常是指向应用程序类加载 AppClassLoadergetClass().getClassLoader() 这个方法返回加载当前类的类加载器,对于大多数非基本类来说,这将是应用程序类加载器 AppClassLoader,而对于 Java 核心库中的类,如 java.lang.Object,则会被引导类加载器 Bootstrap ClassLoader 加载。systemClassLoader 这个变量在当前类初始化是就被赋值了,通常是指向系统类加载器 AppClassLoader 的引用。

//systemClassLoader 的初始化
ClassLoaderWrapper() {try {systemClassLoader = ClassLoader.getSystemClassLoader();} catch (SecurityException ignored) {// AccessControlException on Google App Engine}
}

以上就是解析配置文件的基本流程,最后通过尝试调用各个类加载器的解析配置文件的方法,来对配置文件进行解析,在这个过程中,大量使用了方法重载,使得程序变得灵活

获取 SqlSessionFactory 对象管理数据库连接

在这一步骤中,首先我们可以看到 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 一句中使用 SqlSessionFactoryBuilder 类的 build 方法来构建 SqlSessionFactory 类对象,进入到这个类中可以注意到,它体现了建造者模式的思想,我们看到这个类的核心方法:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {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 parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());

这里面的 parser.parse() 提供了一个 Configuration 类的对象供 SqlSessionFactory 类使用 SqlSessionFactory build(Configuration config) 方法来构造 SqlSessionFactory 对象
可以看 parse 方法的实现细节:

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 firstpropertiesElement(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 #631environmentsElement(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);}
}

可以看到在这里就是配置文件的解析过程,从配置文件的根节点开始,逐层进行解析,也包括其中相关的映射文件,解析过程里,不断把解析结果放入到 Configuration 对象中,最后通过这个 Configuration 对象,构造 SqlSessionFactory 对象。

关键字:MyBatis 源码学习 | Day 2 | MyBatis 初始化

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: