Spring源码分析篇01——Resource

开始之前我先说明几点

1、Spring版本3.1.3

2、我写的会尽量的细

3、我对Spring了解的也没那么清楚,希望能和大家一起学习学习,如果有理解错误的地方,请指出,在这里先谢过了。

我们从XmlBeanFactory开始,虽然在这个版本里已经不被建议使用了,被加上了@Deprecated注解,但是不妨碍我们从这里开始学习。

XmlBeanFactory在使用的时候一般是这样的

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationcontext.xml"));

那么这里的ClassPathResource是个什么东西呢?这一篇我们要说的就是它!

先看下Resource的类图。

《Spring源码分析篇01——Resource》

《Spring源码分析篇01——Resource》

下面直接从代码上来看

new ClassPathResource("applicationcontext.xml")

进入此构造方法

	public ClassPathResource(String path) {
		this(path, (ClassLoader) null);
	}

此构造方法,又调用了另一个构造方法

    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为null的话,会报一个异常

以下是Assert的notNull方法

public static void notNull(Object object, String message) {
		if (object == null) {
			throw new IllegalArgumentException(message);
		}
	}

如果不是null的话,会先调用StringUtils的cleanPath方法

实际上这个方法完全可以当做一道算法题来面试

先大概说下这个方法的作用与算法,然后我们把代码贴上来,在代码里做一些注释就好了。

cleanPath就是将一个路径字符串进行简化,得到一个简单的路径。

比如1/./2/../../../../3/4/././../../../5最终简化为../../../5

考虑到路径可能是这样的file:/1/2/3,那么file:这个前缀是不应该动的。所以算法大致就是这样的。

先拿到前缀,然后对后缀路径进行简化,对后缀路径用slash(斜线,反斜线 inversing slash)进行分割,得到一个路径数组。

对路径数组从后往前进行循环,并初始下(..)的数量为0

如果循环的值为(.),那么忽略

如果循环的值为(..),那么(..)的数量+1

如果循环的值不是(..),再分两种情况,如果(..)的数量大于0,那么(..)数量-1,否则将循环的值加入到一个LinkedList的开头。

循环结束之后,如果(..)的数量还大于0,那么将多出的(..)再加到LinkedList的开头(这个老版本是没有的,所以../太多的时候回出现bug,比如../../../a.txt,会返回一个a.txt,显然是不对的)。最后再将前缀加上来就完事了。

public static String cleanPath(String path) {
	String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR,FOLDER_SEPARATOR);

	// Strip prefix from path to analyze, to not treat it as part of the
	// first path element. This is necessary to correctly parse paths like
	// "file:core/../core/io/Resource.class", where the ".." should just
	// strip the first "core" directory while keeping the "file:" prefix.

	// 处理一些文件前缀。如"file:", "jndi:"等等
	int prefixIndex = pathToUse.indexOf(":");
	String prefix = "";
	if (prefixIndex != -1) {
		prefix = pathToUse.substring(0, prefixIndex + 1);
		pathToUse = pathToUse.substring(prefixIndex + 1);
	}

	// 使用slash分割pathToUse
	String[] pathArray = delimitedListToStringArray(pathToUse,FOLDER_SEPARATOR);

	// 用于储存clean后的path
	List pathElements = new LinkedList();

	// 记录当前还有多少个../
	int tops = 0;
	for (int i = pathArray.length - 1; i >= 0; i--) {

		// 如果是"./",表示当前路径,直接跳过
		if (CURRENT_PATH.equals(pathArray[i])) {
			// do nothing
		} else if (TOP_PATH.equals(pathArray[i])) {
			// 将../的个数+1
			tops++;
		} else {

			// 如果../个数大于0,那么抵消一次
			if (tops > 0) {
				tops--;
			}

			// 如果“../”已经抵消完毕,将此目录放到LinkedList中去,每次都应该插在链表的头部,这样正好就形成了从高到低的目录结构,这是这个算法的重点
			else {
				pathElements.add(0, pathArray[i]);
			}
		}
	}

	// 将多余的../加上来,在Spring的老版本中是没有这行的,所以当相对路径里../过多的时候就会出现Bug了。

	for (int i = 0; i < tops; i++) {
		pathElements.add(0, TOP_PATH);
	}

	// 将前缀加上来并返回
	return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);
}

继续回到public ClassPathResource(String path, ClassLoader classLoader)这个构造方法,下一行会去掉首个斜线(the leading slash),而这里的classLoader为null,所以classLoader为ClassUtils.getDefaultClassLoader()。

public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back to system class loader...
		}
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			cl = ClassUtils.class.getClassLoader();
		}
		return cl;
	}

这里默认使用的是Thread.currentThread().getContextClassLoader(),如果加载不成功,才会使用ClassUtil.class.getClassLoader()。大家可以多去看下类加载器,这个还是挺重要的。

到此,我们在new一个XmlBeanFactory的时候需要的参数Resource是怎么来的,已经清楚了,这个实际上还是非常简单的,主要讲了StringUtils的cleanPath方法。

这里提个小问题,大家可以思考下,Resource使用的是ClasspathResource,而Resource有许多的实现类,为什么不用其他的呢?比如FileSystemResource,还有为什么我们传入ClasspathResource里面的值是相对于src的一个路径呢?

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/zhengliusu/article/details/51052005
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞