Spring源码分析篇01——Resource

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

开始之前我先说明几点

1、Spring版本3.1.3

2、我写的会尽量的细

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

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

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

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

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

先看下Resource的类图。

2019101710079\_1.png

![《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_01_Resource]:


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring源码分析篇01——Resource

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏