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

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

热度:40   发布时间:2024-01-16 14:14:27.0

Spring提供了一套资源加载接口及实现类,结构如下:


顶级接口ResourceLoader仅提供了一个getResource(String location)方法,可以根据一个资源地址加载资源文件,资源地址的表达式可以是以下几种:

1. classpath:前缀开头的表达式,例如: classpath:smart-context.xml

2.“/”开头的表达式,例如:/WEB-INF/classes/smart-context.xml

3. 非“/”开头的表达,例如:WEB-INF/classes/smart-context.xml

4. url协议,例如:file:/D:/ALANWANG-AIA/Horse-workspace/chapter3/target/classes/smart-context.xml

注意,该方法不支持解析ant风格的资源路径表达式。

Spring提供了实现类DefaultResourceLoader,DefaultResourceLoader在实现了以上列举的功能基础上,还为开发者提供了自定义扩展接口ProtocolResolver,开发者可实现该接口定制个性化资源表达式,代码如下:

	@Overridepublic Resource getResource(String location) {Assert.notNull(location, "Location must not be null");for (ProtocolResolver protocolResolver : this.protocolResolvers) {       // 1Resource resource = protocolResolver.resolve(location, this);if (resource != null) {return resource;}}if (location.startsWith("/")) {return getResourceByPath(location);}       //2else if (location.startsWith(CLASSPATH_URL_PREFIX)) {                     //3return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());}else {try {// Try to parse the location as a URL...URL url = new URL(location);                             //4return new UrlResource(url);}catch (MalformedURLException ex) {// No URL -> resolve as resource path.return getResourceByPath(location);                      //5}}}

步骤1,先用扩展协议解析器解析资源地址并返回。举个例子,咱们可以自定义资源解析器来完成带前缀“classpath:”的解析:

首先实现ProtocolResolver接口:

class ClasspathPreProtocolResolver implements ProtocolResolver{private static String CLASS_PATH_PRE="classpath:";		public Resource resolve(String location, ResourceLoader resourceLoader) {if( location.startsWith(CLASS_PATH_PRE)) {return new ClassPathResource(location.substring(CLASS_PATH_PRE.length()));}	   return null;}		}

接着测试:

public class Test {public static void main(String[] args) {	DefaultResourceLoader bf = new DefaultResourceLoader();bf.getProtocolResolvers().add(new ClasspathPreProtocolResolver());		try {System.out.println(bf.getResource("smart-context.xml").getURI().toURL());} catch (Exception e) {e.printStackTrace();} 		}
}

执行结果:

file:/D:/ALANWANG-AIA/Horse-workspace/chapter3/target/classes/smart-context.xml

步骤2,假设location以斜杠开头,则调用该类中 getResourceByPath(String path)方法 ,代码如下:

	protected Resource getResourceByPath(String path) {return new ClassPathContextResource(path, getClassLoader());}

getResourceByPath(String path)方法 返回ClassPathContextResource对象,ClassPathContextResource是该类中的一个内部类:

/*** ClassPathResource that explicitly expresses a context-relative path* through implementing the ContextResource interface.*/protected static class ClassPathContextResource extends ClassPathResource implements ContextResource {public ClassPathContextResource(String path, @Nullable ClassLoader classLoader) {super(path, classLoader);}@Overridepublic String getPathWithinContext() {return getPath();}@Overridepublic Resource createRelative(String relativePath) {String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);return new ClassPathContextResource(pathToUse, getClassLoader());}}

ClassPathContextResource继承自ClassPathResource并实现了ContextResource,因此该类可以表示资源的地址为web容器中的项目的类文件存放路径,假设咱们现有一个web项目叫chapter3,则ClassPathContextResource可以表示/WEB-INF/classes/下的资源文件。咱们可以在web.xml中做如下配置:

<context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/classes/smart-context.xml</param-value>
</context-param>

启动web容器观察输出,成功。

[INFO] Initializing Spring root WebApplicationContext
16:01:24.877 [PathWatcher@679d0be8] INFO  org.springframework.beans.factory.xml.
XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resou
rce [/WEB-INF/classes/smart-context.xml]

步骤三,假如资源表达式以classpath开头,则截取除前缀calsspath:的路径,并做为ClassPathResource的构造参数,生成ClassPathResource实例后返回。咱们可以在web.xml中做如下配置:

<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:smart-context.xml</param-value>
</context-param>

启动服务器,观察输出,成功。

[INFO] Initializing Spring root WebApplicationContext
16:08:01.603 [PathWatcher@679d0be8] INFO  org.springframework.beans.factory.xml.
XmlBeanDefinitionReader - Loading XML bean definitions from class path resource
[smart-context.xml]

步骤四,如果以上的三个步骤都加载失败,则尝试使用url的方式来加载,因此咱们也可以在web.xml做如下配置:

<context-param><param-name>contextConfigLocation</param-name><param-value>file:/D:/ALANWANG-AIA/Horse-workspace/chapter3/target/classes/smart-context.xml</param-value>
</context-param>

启动服务器,观察输出,成功。

[INFO] Initializing Spring root WebApplicationContext
16:11:31.354 [PathWatcher@679d0be8] INFO  org.springframework.beans.factory.xml.
XmlBeanDefinitionReader - Loading XML bean definitions from URL [file:/D:/ALANWA
NG-AIA/Horse-workspace/chapter3/target/classes/smart-context.xml]

步骤五,如果url的方式也加载失败了,则尝试再次使用classpath 的方式加载,这时,咱们的web.xml 可以这样写:

<context-param><param-name>contextConfigLocation</param-name><param-value>WEB-INF/classes/smart-context.xml</param-value>
</context-param>

启动服务器,观察输出,成功。

[INFO] Initializing Spring root WebApplicationContext
16:16:05.256 [PathWatcher@679d0be8] INFO  org.springframework.beans.factory.xml.
XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resou
rce [/WEB-INF/classes/smart-context.xml]

至此,DefaultResourceLoader的代码就分析完了,从分析的过程中咱们可以得出结论:

1.spring对框架的使用者非常友好,很多地方都留有接口供使用者扩展,而且是优先调用。

2.DefaultResourceLoader虽然提供了众多的资源表达式,但是带前缀classpath:在书写上比其他几种更为简便,在执行顺序上位于第二,仅次于扩展接口,那么在效率上也优于其他书写方式(虽然可以忽略不计...)

3.带classpath前缀的写法更易于移植,相对于一般的java程序,classpath指向的是类文件存放的位置,而对于web应用来说,classpath指向的是WEB-INF/classes,因此classpath: 的写法还有屏蔽不同应用类型的作用,实际开发中也以classpath:前缀的写法居多,所以在日后的开发中,我选classpath:前缀。

4.DefaultResourceLoader的getResource(String location)可以看作为是一个模板方法,getResourceByPath(String path) 是钩子函数,只不过DefaultResourceLoader自身也为其提供了实现,

protected Resource getResourceByPath(String path) {return new ClassPathContextResource(path, getClassLoader());}

因此DefaultResourceLoader可以单独使用,也可以搭配子类。子类通过重写getResourceByPath(String path)来返回和自身相关的资源类型,从而达到利用不同方式加载资源的目的。

以上是个人拙见,有错误的地方还望指正,感激不尽,谢谢。