8.3.2 ResourceLoader接口和 ResourceLoaderAware接口

8.3.2 ResourceLoader接口和 ResourceLoaderAware接口

Spring提供如下两个标志性接口。

接口 描述
Resourceloader 该接口实现类的实例可以获得一个Resource实例。
ResourceLoaderAware 该接口实现类的实例将获得一个Resourceloader的引用.

ResourceLoader接口

ResourceLoader接口方法

ResourceLoader接口里只有一个方法,如下所示:

方法 描述
Resource getResource(String location) 该方法用于返回一个Resource实例。

ApplicationContext的实现类都实现ResourceLoader接口,因此ApplicationContext可用于直接获取Resource实例。

某个ApplicationContext实例获取Resource实例时,默认采用与ApplicationContext相同的资源访问策略。看如下代码:

1
2
3
ApplicationContext ctx = new 实现类(
"beans.xml");
Resource resource = ctx.getResource("book.xml");

从上面的代码中无法确定Spring用哪个实现类来访问指定资源,

ApplicationContext使用相同策略访问资源是什么意思

Spring将采用和ApplicationContext相同的策略来访问资源。就是说:

  • 如果这个ApplicationContextFileSystemXmlApplicationContext,则getResource()方法获取到的resource就是FileSystemResource实例;
  • 如果这个ApplicationContextClassPathXmlApplicationContext,则getResource()方法获取到的resource就是ClassPathResource实例;
  • 如果这个ApplicationContextXmlWebApplicationContext,则getResource()方法获取到的resource就是ServletContextResource实例。

从上面的介绍可以看出,当Spring应用需要进行资源访间时,实际上并不需要直接使用Resource实现类,而是调用ResourceLoader实例的getResource()方法来获得资源。 ResourceLoader将会负责选择Resource的实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来,这就是典型的策略模式。

程序示例

1
2
3
4
5
6
7
8
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\ResourceLoader
├─beans.xml
├─book.xml
└─src\
├─beans.xml
├─book.xml
└─lee\
└─ResourceLoaderTest.java

看如下示例程序,将使用ApplicationContext来访问资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package lee;
import org.springframework.context.*;
import org.springframework.context.support.*;
import org.springframework.core.io.Resource;
import org.dom4j.*;
import org.dom4j.io.*;
import java.util.*;

public class ResourceLoaderTest
{
public static void main(String[] args) throws Exception
{
// 创建ApplicationContext实例
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"beans.xml");
// ApplicationContext ctx = new
// FileSystemXmlApplicationContext("beans.xml");
Resource res = ctx.getResource("book.xml");

// 获取该资源的简单信息
System.out.println(res.getFilename());
System.out.println(res.getDescription());
// 创建基于SAX的dom4j解析器
SAXReader reader = new SAXReader();
Document doc = reader.read(res.getFile());
// 获取根元素
Element el = doc.getRootElement();
List l = el.elements();
// 遍历根元素的全部子元素
for (Iterator it = l.iterator(); it.hasNext();)
{
// 每个节点都是<书>节点
Element book = (Element) it.next();
List ll = book.elements();
// 遍历<书>节点的全部子节点
for (Iterator it2 = ll.iterator(); it2.hasNext();)
{
Element eee = (Element) it2.next();
System.out.println(eee.getText());
}
}
}
}

上面程序中的第一行代码创建了一个ApplictionContext对象,第二行代码通过该ApplictionContext对象来获取资源。虽然上面程序并未明确指定采用哪一种Resource实现类,而是仅仅通过ApplicactionContext获得Resource。不过由于使用的是ClassPathApplicationContext来获取资源,所以Spring将会使用ClassPathResource实现类从类加载路径下访问资源。

程序执行结果如下:

1
2
3
4
5
6
7
book.xml
class path resource [book.xml]
疯狂Java讲义
李刚
轻量级Java EE企业应用实战
李刚

从运行结果可以看出, 通过ClassPathXmlApplicationContext获取到的ResourceClassPathResource实现类。
如果将ApplicationContext改为使用FileSystemXmlApplicationContext 实现类,运行上面程序,将看到如下运行结果:

1
2
3
4
5
6
book.xml
file [E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\ResourceLoader\book.xml]
疯狂Java讲义
李刚
疯狂iOS讲义
李刚

从上面的执行结果可以看出,程序的Resource实现类发生了改变,变为使用FileSystemResource实现类。

为了保证得到上面的两次运行结果,需要分别在类加载路径(src/目录)下、当前文件路径(项目路径)下放置beans.xml:和book.xml两个文件(为了区分,本示例故意让两个路径下的book.xml文件略有区别)

使用前缀强制指定使用哪个Resource实现类

另外,使用ApplicationContext访问资源时,也可不理会ApplicationContext的实现类,强制使用指定的ClassPathResourceFileSystemResource等实现类,这可通过不同前缀来指定,如下面的代码所示。

1
2
//通过 classpath:前缀,强制使用ClasspathResource
Resource r= ctx.getResource("classpath:beans.xml");

类似的,还可以使用标准的java.net.URL前缀来强制使用UrlResource,如下所示:

1
2
3
4
//通过标准的file:前缀,强制使用UrlResource访问本地文件资源
Resource res=ctx.getResource("file:beans.xml");
//通过标准的http:前缀,强制使用UrlResource访问基于HTTP协议的网络资源
Resource res=ctx.getResource("http://localhost:8888/beans.xml");

常见前缀及其对应的访问策略

以下是常见的前缀及对应的访问策略。

前缀 描述
classpath: ClassPathResource实例访问类加载路径下的资源。
file: UrlResource实例访问本地文件系统的资源
http: UrlResource实例访问基于HTTP协议的网络资源。
无前缀 ApplicationContext的具体实现类来决定访问策略。

ResourceLoaderAware

ResourceLoaderAware完全类似于Spring提供的BeanFactoryAwareBeanNameAware接口。
ResourceLoaderAware接口提供了一个setResourceLoader()方法,该方法将由Spring容器负责调用,Spring容器会传入一个ResourceLoader对象作为setResourceLoader()方法的参数。
如果把实现ResourceLoaderAware接口的Bean类部署在Spring容器中, Spring容器会将自身当成ResourceLoader作为参数传入setResourceLoader()方法。由于ApplicationContext的实现类都实现了ResourceLoader接口, Spring容器自身完全可作为ResourceLoader使用。

程序示例

1
2
3
4
5
6
7
8
9
10
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\ResourceLoaderAware
└─src\
├─beans.xml
├─lee\
│ └─SpringTest.java
└─org\
└─crazyit\
└─app\
└─service\
└─TestBean.java

例如,如下Bean类实现了ResourceAware接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.crazyit.app.service;

import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;

public class TestBean implements ResourceLoaderAware
{
private ResourceLoader rd;
// 实现ResourceLoaderAware接口必须实现的方法
// 如果把该Bean部署在Spring容器中,该方法将会由Spring容器负责调用
// Spring容器调用该方法时,Spring容器会将自身作为参数传给该方法
public void setResourceLoader(ResourceLoader resourceLoader)
{
System.out.println("--执行setResourceLoader 方法--");
this.rd = resourceLoader;
}
// 返回ResourceLoader对象的引用
public ResourceLoader getResourceLoader()
{
return rd;
}
}

将该类部署在Spring容器中, Spring将会在创建完该Bean的实例之后,自动调用该BeansetResourceLoader()方法,调用该方法时会将容器自身作为参数传入。

如果需要验证这一点,程序可用TestBeangetResourceLoader()方法的返回值与Spring容器进行"=="运算符进行比较,将会发现使用"=="比较返回true,这表明两个引用指向相同的对象.

主类代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package lee;

import org.crazyit.app.service.TestBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ResourceLoader;

public class SpringTest
{
public static void main(String[] args)
{
// 创建ApplicationContext容器
@SuppressWarnings("resource")
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"beans.xml");
// 获取容器中名为test的Bean实例
TestBean tb = ctx.getBean("test", TestBean.class);
// 通过tb实例来获取ResourceLoader对象
ResourceLoader rl = tb.getResourceLoader();
// 判断程序获得ResourceLoader和Spring容器是否是同一个对象
System.out.println(rl == ctx);
}
}

运行结果:

1
2
--执行setResourceLoader 方法--
true