ClassPathResource详解

ClassPathReource resource=new ClassPathResource("spring_beans.xml");

1:public class ClassPathResource extends AbstractFileResolvingResource

在ClassPathResource中,含参数String path的构造函数:

public ClassPathResource(String path ) {

this (path , (ClassLoader) null);

}

2:上述构造函数指向了另外一个构造函数:

public ClassPathResource (String path , ClassLoader classLoader ) {

Assert. notNull(path, "Path must not be null");

String pathToUse = StringUtils.cleanPath(path);

if (pathToUse .startsWith("/")) {

pathToUse = pathToUse .substring(1);

}

this .path = pathToUse;

this .classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());

}

能够看到path由StringUtils的cleanPath方法返回了pathToUse。由此,我们找到StringUtils的cleanPath方法

3:public abstract class StringUtils

public static String cleanPath (String path ) {

if (path == null) {

return null ;

}

String pathToUse = replace( path , WINDOWS_FOLDER_SEPARATOR , FOLDER_SEPARATOR);

int prefixIndex = pathToUse .indexOf(":" );

String prefix = "" ;

if (prefixIndex != -1) {

prefix = pathToUse .substring(0, prefixIndex + 1);

pathToUse = pathToUse .substring(prefixIndex + 1);

}

if (pathToUse .startsWith(FOLDER_SEPARATOR)) {

prefix = prefix + FOLDER_SEPARATOR;

pathToUse = pathToUse .substring(1);

}

String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR );

List<String> pathElements = new LinkedList<String>();

int tops = 0;

for (int i = pathArray. length - 1; i >= 0; i --) {

String element = pathArray [i ];

if (CURRENT_PATH .equals(element)) {

// Points to current directory - drop it.

}

else if (TOP_PATH.equals(element)) {

// Registering top path found.

tops ++;

}

else {

if (tops > 0) {

// Merging path element with element corresponding to top path.

tops --;

}

else {

// Normal path element found.

pathElements .add(0, element );

}

}

}

// Remaining top paths need to be retained.

for (int i = 0; i < tops; i++) {

pathElements .add(0, TOP_PATH);

}

return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR );

}

4:StringUtils类中 replace方法

public static String replace (String inString , String oldPattern , String newPattern ) {

if (!hasLength( inString ) || !hasLength(oldPattern) || newPattern == null ) {

return inString ;

}

StringBuilder sb = new StringBuilder();

int pos = 0; // our position in the old string

int index = inString .indexOf(oldPattern );

// the index of an occurrence we‘ve found, or -1

int patLen = oldPattern.length();

while (index >= 0) {

sb.append( inString .substring(pos , index ));

sb.append( newPattern );

pos = index + patLen;

index = inString .indexOf(oldPattern, pos );

}

sb.append( inString .substring(pos ));

// remember to append any characters to the right of a match

return sb .toString();

}

5:StringUtils类类中的hasLength方法。由此可以看出,同样的方法名,不同的方法签名,然后在其中一个方法中引用另外一个方法。好多类都是这么用的,就像开始的时候的构造函数那样,虽然不知道好处 是什么,但先记下来。

public static boolean hasLength (String str ) {

return hasLength((CharSequence) str);

}

public static boolean hasLength (CharSequence str) {

return (str != null && str.length() > 0);

}

跟踪到这里,可以知道hasLength方法的目的就是str不为空且str的长度大于0。突然发现CharSequence这个类没接触过,来看一下它的源码

6:public interface CharSequence{}

  额,源码没看懂 就不粘贴过来了。

7:回到StringUtils的replace方法

首先判断传入的三个参数,如果为空后者长度小于0,直接返回inString;那么我看一下这三个参数都是什么:

inString :path 这个就是我们传入的文件名

oldPattern:private static final String WINDOWS_FOLDER_SEPARATOR = "\\";

newPattern:private static final String FOLDER_SEPARATOR = "/" ;这两个是文件分隔符

然后给局部变量index赋值,通过查阅API:

public int indexOf(int ch)

返回指定字符在此字符串中第一次出现处的索引。

意思就是在path中查找"\\",例如我写文件的绝对路径是D:\\文件\\API\\JDK_API_1_6_zh_CN.CHM,我就需要循环的读取“\\”,接下来while循环中出现了substring方法,继续查阅API:

public String substring(int beginIndex)

返回一个新的字符串,它是此字符串的一个子字符串。该子字符串从指定索引处的字符开始,直到此字符串末尾。

public String substring(int beginIndex,
                        int endIndex)

返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex

故此 ,第一次循环会把path路径中从0索引开始,直到第一个"\\"之间的内容添加到StringBuffer中,然后再在StringBuffer中添加“/”,接下来pos和index都需要改变,要往后挪。因为循环需要往后走,我们要找到第二个“\\”,觉得这个有点算法的意思。返回sb.toString()。

总结一下replace方法,本意是根据传入的路径path,如果是D:\\文件\\API\\JDK_API_1_6_zh_CN.CHM这种格式的,给转换成D:/文件/API/JDK_API_1_6_zh_CN.CHM这种格式。

8:StringUtils的cleanPath方法:

通过replace的返回值,我们得到了可以用的路径pathToUse,然后我们要把这个路径下“:”给找出来,正如代码

          int prefixIndex = pathToUse.indexOf(":" );

那样,需要知道,indexOf方法只要没找到相应的字符,就会返回-1,所以在下面的判断中才会以perfixIndex是否为-1来进行判断。如果路            径中有“:”,接着以D:/文件/API/JDK_API_1_6_zh_CN.CHM举例,prefix="D:"  pathToUse="/文件/API/JDK_API_1_6_zh_CN.CHM ",这个很有意思,因为程序不知道我们输入的是绝对路径 带D:的这种 ,还是/开头的这种,或者说相对路径,程序直接全给你判断了。接下来会判断pathToUse是否以“/"开头,是的话prefix会加上“/”,现在的prefix有两种情况,可能是"D:/"这种,也可能是"/"这种,而pathToUse肯定是“文件/API/JDK_API_1_6_zh_CN.CHM ”这种了。

String[] pathArray = delimitedListToStringArray( pathToUse, FOLDER_SEPARATOR );

看到这句代码,我估计是把pathToUse给拆成字符串数组里,就像是这样,文件 API ***的这种。接下来看看具体的代码是不是这样:

9:StringUtils的delimitedListToStringArray方法

public static String[] delimitedListToStringArray(String str, String delimiter) {

return delimitedListToStringArray( str, delimiter, null );

}

public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete ) {

if (str == null) {

return new String[0];

}

if (delimiter == null) {

return new String[] {str};

}

List<String> result = new ArrayList<String>();

if ("" .equals(delimiter)) {

for (int i = 0; i < str.length(); i++) {

result.add(deleteAny( str.substring(i , i + 1), charsToDelete));

}

}

else {

int pos = 0;

int delPos ;

while ((delPos = str.indexOf(delimiter , pos )) != -1) {

result.add(deleteAny( str.substring(pos , delPos), charsToDelete ));

pos = delPos + delimiter.length();

}

if (str .length() > 0 && pos <= str.length()) {

// Add rest of String, but not in case of empty input.

result.add(deleteAny( str.substring(pos ), charsToDelete));

}

}

return toStringArray( result);

}

先看看传入的参数:

str:pathToUse,就是文件/API/JDK_API_1_6_zh_CN.CHM

delimiter:"/"

charsToDelete:null

public static String deleteAny(String inString, String charsToDelete ) {

if (!hasLength( inString) || !hasLength(charsToDelete)) {

return inString ;

}

StringBuilder sb = new StringBuilder();

for (int i = 0; i < inString.length(); i++) {

char c = inString.charAt( i);

if (charsToDelete .indexOf(c) == -1) {

sb.append( c);

}

}

return sb .toString();

}

如果说我们传入的"/"等于""的话,显然是不可能,我们所假如的话,会把pathTOUse倒着循环,每个字符都摘出来,然后当成字符串用,传入deleteAny中,然后又是循环,对每个字符而言,如果charsToDelete中没有这个字符,就在StringBuilder中添加这个字符。返回值是String。当然了,这个还没用到。我们用到的是那个很复杂的else

我们遇到了一个循环,对pathToUse而言,从索引0开始,如果pathToUse中有"/",就像文件/API/JDK_API_1_6_zh_CN.CHM 我们会得到“文件,然后还会进入deleteAny这个方法,参数inString就是"文件",charsToDelete是null,突然发现charsToDelete的值为Null的话会直接返回InString,也就是“文件”。返回到delimitedListToStringArray方法之后,接着往后循环,最终的结果就是实现了把pathToUse给切割成若干个String的形式。

10:回到StringUtils的cleanPath方法

我们遇到了一个倒着的循环,如果说我们这个是特别正常的路径,就相当于复制了,如果是以.或者..结尾的这些内容,我们就把它给忽略了。

我得承认上面这些过程真的好复杂,其实就是做了一件事,对输入的路径进行了处理,只不过考虑的情况多了一点。所以我决定还是用debug来走一遍看看。

这时我用的是绝对路径

终于熬到了这个方法的结束。

11:回到ClassPathResource的构造函数

this .classLoader = ( classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());

如果传入的classLoaser有值,就返回这个值,如果没有,就获取一个。

public static ClassLoader getDefaultClassLoader() {

ClassLoader cl = null;

try {

cl = Thread.currentThread().getContextClassLoader();

}

catch (Throwable ex ) {

// Cannot access thread context ClassLoader - falling back...

}

if (cl == null) {

// No thread context class loader -> use class loader of this class.

cl = ClassUtils.class .getClassLoader();

if (cl == null) {

// getClassLoader() returning null indicates the bootstrap ClassLoader

try {

cl = ClassLoader.getSystemClassLoader();

}

catch (Throwable ex ) {

// Cannot access system ClassLoader - oh well, maybe the caller can live with null...

}

}

}

return cl ;

}

写到这,我们的ClassPathResouce resouce实例就有了path 和 classLoader这两个关键属性。

如果我们想获取输入流

@Override

public InputStream getInputStream() throws IOException {

InputStream is;

if (this .clazz != null) {

is = this.clazz .getResourceAsStream(this. path);

}

else if (this.classLoader != null) {

is = this.classLoader .getResourceAsStream(this. path);

}

else {

is = ClassLoader.getSystemResourceAsStream( this.path );

}

if (is == null) {

throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");

}

return is ;

}

会判断clazz 有没有值,classLoader有没有值,然后再获取输入流。前两天整理了java.lang.Class这个类的意思,现在就能用一点了,先看看定义的一些属性

private final String path ;

private ClassLoader classLoader;

private Class<?> clazz;

ClassPathResource有好几个构造函数,有的构造函数会传入classLoader,有的会传入clazz,这个clazz就是相应的类在JVM上的实例,显然上面的例子中并没有这个东西,而classLoader是有的,所以通过classLoader获取输入流。我觉得对ClassPathResource理解的更透彻了,虽然大部分时间都是在对path进行处理。近期还要看看ClassLoader,还不是很清楚它的工作机制。

时间: 2024-11-01 17:50:48

ClassPathResource详解的相关文章

Spring Cache抽象详解

缓存简介 缓存,我的理解是:让数据更接近于使用者:工作机制是:先从缓存中读取数据,如果没有再从慢速设备上读取实际数据(数据也会存入缓存):缓存什么:那些经常读取且不经常修改的数据/那些昂贵(CPU/IO)的且对于相同的请求有相同的计算结果的数据.如CPU--L1/L2--内存--磁盘就是一个典型的例子,CPU需要数据时先从L1/L2中读取,如果没有到内存中找,如果还没有会到磁盘上找.还有如用过Maven的朋友都应该知道,我们找依赖的时候,先从本机仓库找,再从本地服务器仓库找,最后到远程仓库服务器

Spring的资源详解

一.Spring的资源详解 1.1引言 在日常程序开发中,处理外部资源是很繁琐的事情,我们可能需要处理URL资源.File资源.ClassPath相关资源.服务器相关资源等等很多资源.因此处理这些资源需要使用不同的接口,这就增加了我们系统的复杂性:而且处理这些资源步骤都是类似的(打开资源.读取资源.关闭资源),因此如果能抽象出一个统一的接口来对这些底层资源进行统一访问,是不是很方便,而且使我们系统更加简洁,都是对不同的底层资源使用同一个接口进行访问. Spring提供一个Resource接口来统

Srping讲解----------IoC详解

2.1.1  IoC是什么 Ioc-Inversion of Control,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制.如何理解好Ioc呢?理解好Ioc的关键是要明确"谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了",那我们来深入分析一下: ●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动

Spring IOC源码详解之容器初始化

Spring IOC源码详解之容器初始化 上篇介绍了Spring IOC的大致体系类图,先来看一段简短的代码,使用IOC比较典型的代码 ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDe

【框架】[Spring3]下载安装、开源框架与IoC控制反转详解

转载请注明出处:http://blog.csdn.net/qq_26525215 本文源自[大学之旅_谙忆的博客] 昨天刚刚初学Spring3,也许Spring3有点老了哈,不过还是先把3学了再去学习4吧,首先先介绍一下如何去下载Spring的必须包吧. (本篇博客适用于初学Spring的朋友) java spring4现在不推荐使用xml配置文件- 当然啦,这些知识点在Spring4还是可以用的. 不过我在这里详解的还是Spring3哈,见谅~ 下载SpringJAR包/文档: Spring官

【第二章】IoC的基础与详解(一)

2.1.1  IoC是什么 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制.如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下: ●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象:而IoC是有专

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

转载:DenseNet算法详解

原文连接:http://blog.csdn.net/u014380165/article/details/75142664 参考连接:http://blog.csdn.net/u012938704/article/details/53468483 本文这里仅当学习笔记使用,具体细节建议前往原文细度. 论文:Densely Connected Convolutional Networks 论文链接:https://arxiv.org/pdf/1608.06993.pdf 代码的github链接:h

MariaDB(MySQL)创建、删除、选择及数据类型使用详解

一.MariaDB简介(MySQL简介略过) MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品.在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB. MariaDB由MySQL的创始人Michael Widenius(英语:Michael Widenius)主导开发,他早前曾以10亿美元的价格,将自己创建的公司MySQL A