当前位置: 代码迷 >> 综合 >> Spring5:就这一次,搞定资源加载器之ClassRelativeResourceLoader
  详细解决方案

Spring5:就这一次,搞定资源加载器之ClassRelativeResourceLoader

热度:83   发布时间:2024-01-16 14:14:11.0

ClassRelativeResourceLoader继承自DefaultResourceLoader,并重写了getResourceByPath(String path),代码如下:

 * Copyright 2002-2012 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.core.io;import org.springframework.util.Assert;
import org.springframework.util.StringUtils;/*** {@link ResourceLoader} implementation that interprets plain resource paths* as relative to a given {@code java.lang.Class}.** @author Juergen Hoeller* @since 3.0* @see Class#getResource(String)* @see ClassPathResource#ClassPathResource(String, Class)*/
public class ClassRelativeResourceLoader extends DefaultResourceLoader {private final Class<?> clazz;/*** Create a new ClassRelativeResourceLoader for the given class.* @param clazz the class to load resources through*/public ClassRelativeResourceLoader(Class<?> clazz) {Assert.notNull(clazz, "Class must not be null");this.clazz = clazz;setClassLoader(clazz.getClassLoader());}@Overrideprotected Resource getResourceByPath(String path) {return new ClassRelativeContextResource(path, this.clazz);}/*** ClassPathResource that explicitly expresses a context-relative path* through implementing the ContextResource interface.*/private static class ClassRelativeContextResource extends ClassPathResource implements ContextResource {private final Class<?> clazz;public ClassRelativeContextResource(String path, Class<?> clazz) {super(path, clazz);this.clazz = clazz;}@Overridepublic String getPathWithinContext() {return getPath();}@Overridepublic Resource createRelative(String relativePath) {String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);return new ClassRelativeContextResource(pathToUse, this.clazz);}}}

分析DefaultResourceLoader的时候咱们讲过,子类可以通过重写getResourceByPath(String path)来返回和自身相关的资源类型,ClassRelativeResourceLoader返回的是ClassRelativeContextResource对象,ClassRelativeContextResource对象表示上下文相对路径,因此猜测ClassRelativeResourceLoader具有从给定的class所在的路径下加载资源的能力,咱们来个例子验证下,

假设咱们现在有一个controller叫做LoginController,在com.smart.web包下,该包下有一个名为test.xml文件,如果需要加载这个文件咱们就可以这么写:

package com.smart.web;import java.io.IOException;import org.springframework.core.io.ClassRelativeResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class LoginController  {@RequestMapping(value="/index.html")public String loginPage() throws IOException {ResourceLoader resourceLoader=new ClassRelativeResourceLoader(this.getClass());Resource resource=resourceLoader.getResource("test.xml");System.out.println(resource.getFile().getPath());return "index";}   }

ClassRelativeResourceLoader的构造器接收一个class,意思是将从传入的class所在路径或子路径下加载资源,这里咱们把LoginController的class传给它。

启动服务器,访问index.html,观察输出:

D:\ALANWANG-AIA\Horse-workspace\chapter3\target\classes\com\smart\web\test.xml

从打印信息上看到,成功。

那么如果咱们传给ClassRelativeResourceLoader的是另一个包下的class,会发生什么情况呢?

假设还有一个包叫做com.smart.dao,该包下有个类叫UserDao,咱们把UserDao的class传进去看看:

package com.smart.web;import java.io.IOException;import org.springframework.core.io.ClassRelativeResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;import com.smart.dao.UserDao;@Controller
public class LoginController  {@RequestMapping(value="/index.html")public String loginPage() throws IOException {ResourceLoader resourceLoader=new ClassRelativeResourceLoader(UserDao.class);Resource resource=resourceLoader.getResource("test.xml");System.out.println(resource.getFile().getPath());return "index";}   }

访问index.html ,发现报错了,

Caused by:
java.io.FileNotFoundException: class path resource [com/smart/dao/test.xml] cannot be resolved to URL because it does not existat org.springframework.core.io.ClassPathResource.getURL(ClassPathResource.java:192)

从输出信息中可以看,它取com.smart.dao.下找test.xml 了,因为该包下没有这个文件,所以报错了,咱们把test.xml扔到com.smart.dao下再试一次:

D:\ALANWANG-AIA\Horse-workspace\chapter3\target\classes\com\smart\dao\test.xml

发现可以正常输出了。

那么假如咱们传一个相对路径进去呢?,比如test/test.xml,咱们试一下:

[WARNING] /chapter3/index.html java.io.FileNotFoundException: class path resourc
e [com/smart/dao/test/test.xml] cannot be resolved to URL because it does not ex
ist

发现报错了,咱们在com.smart.dao下新建个包test,然后把test.xml扔进去试一下:

D:\ALANWANG-AIA\Horse-workspace\chapter3\target\classes\com\smart\dao\test\test.
xml

发现又可以了。

那么咱们传一个绝对路径给它呢?像这样:/test/test.xml:

[WARNING] /chapter3/index.html java.io.FileNotFoundException: class path resourc
e [test/test.xml] cannot be resolved to URL because it does not exist

发现报错了,而且提示信息与以往稍有些不同,之前是  [com/smart/dao/test/test.xml]

现在的报错信息是[test/test.xml],

说明如果以斜杠开头的话就会从classpath路径下找,咱们打开父类的模板方法一看就明白:

if (location.startsWith("/")) {return getResourceByPath(location);}else if (location.startsWith(CLASSPATH_URL_PREFIX)) {return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());}

这段代码来自DefaultResourceLoader中的getResource(String location),根据这段代码咱们可以知道只要是以斜杠打头的资源路径都会被当作classpath路径处理,只有非斜杠开头,非classpath:前缀开头,才有可能被子类的getResourceByPath处理,那么接下来其他子类的分析,有关于这块的就此略过不提。

最后,从以上的测试中咱们可以总结出,ClassRelativeResourceLoader扩展的功能是可以根据给定的class所在包或者所在包的子包下加载资源。

最后附上个链接,作者举了很多例子供咱们参考:点击打开链接

以上纯属个人理解,如有错误的地方,还望多多指正,谢谢。