news 2026/9/1 21:16:55

5-5 Mybatis的Resources和Spring中的Resource有什么关联吗

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
5-5 Mybatis的Resources和Spring中的Resource有什么关联吗

背景

小白:我们在单独使用Mybatis时,加载配置文件时使用的是Resources类,但Spring集成Mybatis时并没有使用Mybatis的Resources类来加载配置,而是利用Spring封装的Resource解析器来读取Mybatis的配置,这是为什么呢?

扫地僧:从功能上来讲,都是相同的;从加载对象上来看,Mybatis加载配置文件,Spring可以加载所有资源文件;总体上说,Spring因面临的资源对象种类更丰富,它需要更详细的资源分类体系,而Mybatis的资源比较固定,实现起来更简单。下面让我们探探它们的内部原理。

Mybatis中的Resources

构建SqlSessionFactory通常有两种方式:

1.使用Reader读取文件

Readerreader=Resources.getResourceAsReader("com/davidwang456/SqlMapConfig.xml");SqlSessionFactorysqlSessionFactory=newSqlSessionFactoryBuilder().build(reader);

2.使用InputStream读取文件

InputStreaminputStream=Resources.getResourceAsStream("com/davidwang456/SqlMapConfig.xml");SqlSessionFactorysqlSessionFactory=newSqlSessionFactoryBuilder().build(inputStream);

我们发现Mybatis提供了一个公共的Resources类读取配置文件。

Resources

Resources类位于org.apache.ibatis.io包下,用于处理相关的IO操作,例如将文件,网络资源进行读取并转换为File,InputStream,Reader,URL等Java类。Resources类提供了多个getResourceAsxxx()方法作为对外暴露的方法,Resources并不提供方法实现,而是内部维护着一个静态ClassLoaderWrapper实例,Resources中的方法都是通过调用ClassLoaderWrapper相关方法实现。下面列出了Resources最常用的方法。

方法名参数
File getResourceAsFile()resource,classloader
Properties getResourceAsProperties()resource,classloader
Reader getResourceAsReader()resource,classloader
InputStream getResourceAsStream()resource,classloader
URL getResourceURL()resource,classloader
Properties getUrlAsProperties()resource,classloader
setCharset()Charset
void setDefaultClassLoader(ClassLoader)ClassLoader
示例1
Propertiesp=Resources.getResourceAsProperties(null,"com/davidwang456/SqlMapConfig.xml");

此时属性p的值为:

{</configuration>=,<transactionManager=type="JDBC"/>,</environments>=,</environment>=,<environments=default="development">,<mappers>=,</dataSource>=,<dataSource=type="POOLED">,</mappers>=,<environment=id="development">,<mapper=resource="com/davidwang456/Student.xml"/>,<configuration>=,<?xml=version="1.0"encoding="UTF-8"?>,<!DOCTYPE=configurationPUBLIC"-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">,<property=name="password"value="root"/>}
示例2
Filef=Resources.getResourceAsFile(null,"com/davidwang456/SqlMapConfig.xml");System.out.println(f.getAbsolutePath());

此时运行结果为:

C:\workspace\mybatis-3\target\classes\com\davidwang456\SqlMapConfig.xml
示例3
URLurl=Resources.getResourceURL(null,"com/davidwang456/SqlMapConfig.xml");System.out.println(url);

此时运行结果为:

file:/C:/workspace/mybatis-3/target/classes/com/davidwang456/SqlMapConfig.xml

通过追踪源码,我们发现上述的实现由ClassLoaderWrapper实现,如Reader和InputStream的内部实现均为getResourceAsStream

/** * 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 */publicstaticInputStreamgetResourceAsStream(ClassLoaderloader,Stringresource)throwsIOException{InputStreamin=classLoaderWrapper.getResourceAsStream(resource,loader);if(in==null){thrownewIOException("Could not find resource "+resource);}returnin;}

ClassLoaderWrapper

ClassLoaderWrapper没有构造器,无法直接进行实例化,只能通过Resources类进行获取。该类提供了获取资源的两种方式,以InputStream形式读取本地资源,以URL形式读取本地资源。除此之外,ClassLoaderWrappe还提供了classForName()方法用于获取类路径对应的Class实例。下面是ClassLoaderWrapper提供的所有方法。可以看到,每种类型方法都提供了三种入参形式,其中一种方法需要提供一个ClassLoader数组,需要注意的是这种方法并不能直接调用,前两种方法都是交给带有classloader[]参数的方法进行实现的。如果不提供ClassLoader则设置为null。

  • Class<?> classForName(String name)
  • Class<?> classForName(String name, ClassLoader classLoader)
  • Class<?> classForName(String name, ClassLoader[] classLoader)
  • InputStream getResourceAsStream(String resource)
  • InputStream getResourceAsStream(String resource, ClassLoader classLoader)
  • InputStream getResourceAsStream(String resource, ClassLoader[] classLoader)
  • URL getResourceAsURL(String resource)
  • URL getResourceAsURL(String resource, ClassLoader classLoader)
  • URL getResourceAsURL(String resource, ClassLoader[] classLoader)
  • ClassLoader[] getClassLoaders(ClassLoader classLoader)

**getClassLoaders()**用于为getResouceAsxxx()方法提供多个加载器。前面说过,不传入加载器数组的方法都会调用 getResourceAsxxx(String resource, ClassLoader[] classLoader)相关的方法,而获取加载器数组的方式就是直接调用getClassLoaders()方法。

ClassLoader[]getClassLoaders(ClassLoaderclassLoader){returnnewClassLoader[]{classLoader,//自定义加载器defaultClassLoader,Thread.currentThread().getContextClassLoader(),//当前线程加载器getClass().getClassLoader(),//类加载器systemClassLoader//系统默认加载器};}

**classForName()**根据类路径生成Class实例,该方法的本质是调用classloader下的forname方法进行实例化的。

/** * Attempt to load a class from a group of classloaders * * @param name - the class to load * @param classLoader - the group of classloaders to examine * @return the class * @throws ClassNotFoundException - Remember the wisdom of Judge Smails: Well, the world needs ditch diggers, too. */Class<?>classForName(Stringname,ClassLoader[]classLoader)throwsClassNotFoundException{for(ClassLoadercl:classLoader){if(null!=cl){try{Class<?>c=Class.forName(name,true,cl);if(null!=c){returnc;}}catch(ClassNotFoundExceptione){// we'll ignore this until all classloaders fail to locate the class}}}thrownewClassNotFoundException("Cannot find class: "+name);}

**getResourceAsStream()和getResourceAsURL()😗*这两个方法都是获取相关的资源,与File类不同的是,这两个方法传递的参数必须是相对路径,否则抛出异常。同classForName()一样,必须遍历classLoader数组,然后调用getResourceAsStream()和getResource()。

ClassLoaderWrapper内部封装了JDK内部java.lang.ClassLoader的实现。

ClassLoader

ClassLoaderWrapper的getResourceAsStream调用了ClassLoader的方法getResourceAsStream:

/** * Returns an input stream for reading the specified resource. * * <p> The search order is described in the documentation for {@link * #getResource(String)}. </p> * * @param name * The resource name * * @return An input stream for reading the resource, or <tt>null</tt> * if the resource could not be found * * @since 1.1 */publicInputStreamgetResourceAsStream(Stringname){URLurl=getResource(name);try{returnurl!=null?url.openStream():null;}catch(IOExceptione){returnnull;}}

ClassLoaderWrapper的getResourceAsURL调用了ClassLoader的方法getResourceAsURL:

/** * Finds the resource with the given name. A resource is some data * (images, audio, text, etc) that can be accessed by class code in a way * that is independent of the location of the code. * * <p> The name of a resource is a '<tt>/</tt>'-separated path name that * identifies the resource. * * <p> This method will first search the parent class loader for the * resource; if the parent is <tt>null</tt> the path of the class loader * built-in to the virtual machine is searched. That failing, this method * will invoke {@link #findResource(String)} to find the resource. </p> * * @apiNote When overriding this method it is recommended that an * implementation ensures that any delegation is consistent with the {@link * #getResources(java.lang.String) getResources(String)} method. * * @param name * The resource name * * @return A <tt>URL</tt> object for reading the resource, or * <tt>null</tt> if the resource could not be found or the invoker * doesn't have adequate privileges to get the resource. * * @since 1.1 */publicURLgetResource(Stringname){URLurl;if(parent!=null){url=parent.getResource(name);}else{url=getBootstrapResource(name);}if(url==null){url=findResource(name);}returnurl;}

Spring中的Resource

Spring集成Mybatis时并没有使用Mybatis中的Resources类加载配置文件,而是利用了自己的Resource来实现配置文件的加载。

packagecom.davidwang456.mybatis.spring;importjavax.sql.DataSource;importorg.apache.ibatis.session.SqlSessionFactory;importorg.mybatis.spring.SqlSessionFactoryBean;importorg.mybatis.spring.annotation.MapperScan;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.io.support.PathMatchingResourcePatternResolver;importorg.springframework.jdbc.datasource.DataSourceTransactionManager;importcom.alibaba.druid.pool.DruidDataSource;@Configuration@MapperScan("com.davidwang456.mybatis.spring.mapper")publicclassSpringConfig{@BeanpublicDataSourcegetDataSource(){DruidDataSourcedataSource=newDruidDataSource();dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/davidwang456?characterEncoding=UTF-8&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC");dataSource.setUsername("root");dataSource.setPassword("wangwei456");returndataSource;}@BeanpublicDataSourceTransactionManagertransactionManager(){returnnewDataSourceTransactionManager(getDataSource());}@BeanpublicSqlSessionFactorysqlSessionFactory()throwsException{PathMatchingResourcePatternResolverresolver=newPathMatchingResourcePatternResolver();SqlSessionFactoryBeansessionFactory=newSqlSessionFactoryBean();sessionFactory.setDataSource(getDataSource());sessionFactory.setConfigLocation(resolver.getResource("SqlMapConfig.xml"));sesionFactory.setMapperLocations(resolver.getResource("StudentMapper.xml"));returnsessionFactory.getObject();}}

我们深入源码看一看这个PathMatchingResourcePatternResolver是如何加载配置文件的?

@OverridepublicResourcegetResource(Stringlocation){returngetResourceLoader().getResource(location);}/** * Return the ResourceLoader that this pattern resolver works with. */publicResourceLoadergetResourceLoader(){returnthis.resourceLoader;}

其中,resourceLoader由DefaultResourceLoader实现,DefaultResourceLoader内部封装了java.lang.ClassLoader。

不同于Mybatis针对classpath下的资源文件,Spring对资源做了更多的封装,ResourceUtils定义了Spring支持的资源类型:

/** Pseudo URL prefix for loading from the class path: "classpath:" */publicstaticfinalStringCLASSPATH_URL_PREFIX="classpath:";/** URL prefix for loading from the file system: "file:" */publicstaticfinalStringFILE_URL_PREFIX="file:";/** URL prefix for loading from a jar file: "jar:" */publicstaticfinalStringJAR_URL_PREFIX="jar:";/** URL prefix for loading from a war file on Tomcat: "war:" */publicstaticfinalStringWAR_URL_PREFIX="war:";/** URL protocol for a file in the file system: "file" */publicstaticfinalStringURL_PROTOCOL_FILE="file";/** URL protocol for an entry from a jar file: "jar" */publicstaticfinalStringURL_PROTOCOL_JAR="jar";/** URL protocol for an entry from a war file: "war" */publicstaticfinalStringURL_PROTOCOL_WAR="war";/** URL protocol for an entry from a zip file: "zip" */publicstaticfinalStringURL_PROTOCOL_ZIP="zip";/** URL protocol for an entry from a WebSphere jar file: "wsjar" */publicstaticfinalStringURL_PROTOCOL_WSJAR="wsjar";/** URL protocol for an entry from a JBoss jar file: "vfszip" */publicstaticfinalStringURL_PROTOCOL_VFSZIP="vfszip";/** URL protocol for a JBoss file system resource: "vfsfile" */publicstaticfinalStringURL_PROTOCOL_VFSFILE="vfsfile";/** URL protocol for a general JBoss VFS resource: "vfs" */publicstaticfinalStringURL_PROTOCOL_VFS="vfs";/** File extension for a regular jar file: ".jar" */publicstaticfinalStringJAR_FILE_EXTENSION=".jar";/** Separator between JAR URL and file path within the JAR: "!/" */publicstaticfinalStringJAR_URL_SEPARATOR="!/";/** Special separator between WAR URL and jar part on Tomcat */publicstaticfinalStringWAR_URL_SEPARATOR="*/";

这些类型是和Spring应用息息相关的,但Mybatis用不到这么多。Spring底层实现这些资源的读取逻辑如下:

@OverridepublicResourcegetResource(Stringlocation){Assert.notNull(location,"Location must not be null");for(ProtocolResolverprotocolResolver:getProtocolResolvers()){Resourceresource=protocolResolver.resolve(location,this);if(resource!=null){returnresource;}}if(location.startsWith("/")){returngetResourceByPath(location);}elseif(location.startsWith(CLASSPATH_URL_PREFIX)){returnnewClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()),getClassLoader());}else{try{// Try to parse the location as a URL...URLurl=newURL(location);return(ResourceUtils.isFileURL(url)?newFileUrlResource(url):newUrlResource(url));}catch(MalformedURLExceptionex){// No URL -> resolve as resource path.returngetResourceByPath(location);}}}

Spring将支持的资源做了分类,不同的解析器做不同的事情。

总结

  • Mybatis和Spring对资源的访问内部都是通过ClassLoader实现的。
  • Mybatis和Spring因访问资源的场景不同,实现资源的读取逻辑也不同,Mybatis更简单,Spring更成体系。
  • Mybaits的Resources类封装了资源的访问,和Spring中定义的Resource是不同的,Resources更接近于Spring中的DefaultResourceLoader。

延伸阅读与资源

Java 工程师进阶:从 JVM 生产排障到OpenJDK原理
NumPy 从入门到生产落地:全链路实战指南(科学计算/向量化)
Redis 8 实战精讲:从 CRUD 到源码,构建高可用缓存系统
Redis 实战修炼与原理进阶
Python 3实战精进:从脚本到高并发订单引擎
python入门:Rquests从菜鸟脚本到企业级SDK的网络实战圣经
Milvus向量数据库实战修炼:从 0 到 1精通向量检索与生产落地
MongoDB 实战进阶与内核修炼
后端工程师的 AI 转型第一课:Ollama 与私有化大模型实战
10倍开发者的 Dify 魔法书:从零构建全栈 AI 应用
后端工程师转型AI第一课-Ollama 与私有化大模型实战
大型语言模型(LLM) vLLM 高性能推理落地实战
Agent开发之LlamaIndex 实战修炼与源码进阶
大语言模型Transformers 实战修炼与源码剖析

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 21:15:52

安卓手机解压夸克网盘分卷压缩包全攻略:从原理到实践

在安卓手机上处理从夸克网盘下载的分卷压缩包&#xff0c;是很多朋友都会遇到的场景。无论是分享的学习资料、游戏资源&#xff0c;还是大型软件安装包&#xff0c;为了适应网盘传输&#xff0c;它们常常被分割成多个 .part1.rar 、 .z01 这样的小文件。面对一堆零散的文件…

作者头像 李华
网站建设 2026/9/1 21:15:06

奇安信2020开发岗笔试考点拆解:从C/C++到安全基础

先说明一个事情&#xff1a;奇安信2020年的软件开发工程师岗位笔试&#xff0c;和市面上大多数互联网公司的笔试风格不太一样。它不是单纯刷LeetCode就能过的&#xff0c;里面掺杂了大量安全基础、系统底层、网络协议的题目&#xff0c;很多科班出身、算法题刷得很顺的同学&…

作者头像 李华
网站建设 2026/9/1 21:14:23

划子网不用手算了:IT-Tools 3 个网络工具上手记

划子网不用手算了&#xff1a;IT-Tools 3 个网络工具上手记 【免费下载链接】it-tools Collection of handy online tools for developers, with great UX. 项目地址: https://gitcode.com/GitHub_Trending/ittoo/it-tools IT-Tools 网络工具是我值班时用得最多的一套&…

作者头像 李华
网站建设 2026/9/1 21:10:43

Bonsai-demo快速上手:一条命令启动本地AI助手完全教程

Bonsai-demo快速上手&#xff1a;一条命令启动本地AI助手完全教程 【免费下载链接】Bonsai-demo Bonsai Demo 项目地址: https://gitcode.com/GitHub_Trending/bo/Bonsai-demo Bonsai-demo 是一个让 Bonsai 超轻量语言模型在本地跑起来的开源演示项目&#xff1a;只需一…

作者头像 李华
网站建设 2026/9/1 21:08:01

微信小程序打卡签到案例源码解析:从解压到真机预览全流程

简介&#xff1a;本资源是一套完整的微信小程序‘易打卡签到’功能实战源码&#xff0c;面向小程序开发初学者与移动应用开发者&#xff0c;聚焦用户身份管理、签到逻辑实现、前后端数据交互等核心场景&#xff0c;助力快速掌握小程序工程化开发流程。压缩包共81个文件&#xf…

作者头像 李华
网站建设 2026/9/1 21:07:56

微信小程序打卡签到源码拆解:从核心逻辑到业务改造实战

简介&#xff1a;本资源是面向微信小程序初学者与移动开发实践者的「易打卡签到」完整项目源码&#xff0c;聚焦日常办公、校园管理等轻量级考勤场景&#xff0c;提供可运行、可调试、可二次开发的实战范例。压缩包共81个文件&#xff0c;含11个JS逻辑文件&#xff08;涵盖页面…

作者头像 李华