前言
作为Spring提供的较之BeanFactory
更为先进的IoC容器实现,ApplicationContext
除了拥有BeanFactory
支持的所有功能之外,还进一步扩展了基本容器的功能,包括BeanFactoryPostProcessor
、BeanPostProcessor
以及其他特殊类型bean的自动识别、容器启动后bean实例的自动初始化、国际化的信息支持、容器内事件发布等。真是“青出于蓝而胜于蓝”啊!
Spring为基本的BeanFactory
类型容器提供了XmlBeanFactory
实现。相应地,它也为ApplicationContext
类型容器提供了以下几个常用的实现。
-
org.springframework.context.support.FileSystemXmlApplicationContext
。在默认情况下,从文件系统加载bean定义以及相关资源的ApplicationContext
实现。 -
org.springframework.context.support.ClassPathXmlApplicationContext
。在默认情况下,从Classpath加载bean定义以及相关资源的ApplicationContext
实现。 -
org.springframework.web.context.support.XmlWebApplicationContext
。Spring提供的用于Web应用程序的ApplicationContext
实现,我们将在第六部分更多地接触到它。
更多实现可以参照org.springframework.context.ApplicationContext
接口定义的Javadoc,这里不再赘述。
第4章中说明了ApplicationContext
所支持的大部分功能。下面主要围绕ApplicationContext
较之BeanFactory
特有的一些特性展开讨论,
即国际化(I18n)信息支持、统一资源加载策略以及容器内事件发布等。
统一资源加载策略
要搞清楚Spring为什么提供这么一个功能,还是从JavaSE提供的标准类java.net.URL
说起比较好。URL全名是Uniform Resource Locator
(统一资源定位器),但多少有些名不副实的味道。
首先,说是统一资源定位,但基本实现却 只限于网络形式发布的资源的查找和定位工作,基本上只提供了基于HTTP、FTP、File等协议(sun.net.www.protocol
包下所支持的协议)的资源定位功能。虽然也提供了扩展的接口,但从一开始,其自身的“定位”就已经趋于狭隘了。实际上,资源这个词的范围比较广义,资源可以任何形式存在,如以二进制对象形式存在、以字节流形式存在、以文件形式存在等;而且,资源也可以存在于任何场所,如存在于文件系统、存在于Java应用的Classpath中,甚至存在于URL可以定位的地方。
其次,从某些程度上来说, 该类的功能职责划分不清,资源的查找和资源的表示没有一个清晰的界限。当前情况是,资源查找后返回的形式多种多样,没有一个统一的抽象。理想情况下,资源查找完成后,返回给客户端的应该是一个统一的资源抽象接口,客户端要对资源进行什么样的处理,应该由资源抽象接口来界定,而不应该成为资源的定位者和查找者同时要关心的事情。
所以,在这个前提下,Spring提出了一套基于org.springframework.core.io.Resource
和org.springframework.core.io.ResourceLoader
接口的资源抽象和加载策略 。
Spring中的Resource
Spring框架内部使用org.springframework.core.io.Resource
接口作为 所有资源的抽象和访问接口,我们之前在构造BeanFactory的时候已经接触过它,如下代码:
其中ClassPathResource
就是Resource的一个特定类型的实现,代表的是位于Classpath中的资源。
Resource
接口可以根据资源的不同类型,或者资源所处的不同场合,给出相应的具体实现。Spring框架在这个理念的基础上,提供了一些实现类(可以在org.springframework.core.io
包下找到这6些实现类)。
-
ByteArrayResource
。将字节(byte)数组提供的数据作为一种资源进行封装,如果通过InputStream形式访问该类型的资源,该实现会根据字节数组的数据,构造相应的ByteArrayInputStream
并返回。 -
ClassPathResource
。该实现从Java应用程序的ClassPath中加载具体资源并进行封装,可以使用指定的类加载器(ClassLoader)或者给定的类进行资源加载。 -
FileSystemResource
。对java.io.File类型的封装,所以,我们可以以文件或者URL的形式对该类型资源进行访问,只要能跟File打的交道,基本上跟FileSystemResource
也可以。 -
UrlResource
。通过java.net.URL
进行的具体资源查找定位的实现类,内部委派URL进行具体的资源操作。 -
InputStreamResource
。将给定的InputStream
视为一种资源的Resource实现类,较为少用。
如果以上这些资源实现还不能满足要求,那么我们还可以根据相应场景给出自己的实现,只需实现org.springframework.core.io.Resource
接口就是了。不过,要真想实现自定义的Resource,倒是真没必要直接实现该接口,我们可以继承org.springframework.core.io.AbstractResource
抽象类,然后根据当前具体资源特征,覆盖相应的方法就可以了。
ResourceLoader ,“更广义的URL”
资源是有了,但如何去查找和定位这些资源,则应该是ResourceLoader
的职责所在了。**org.springframework.core.io.ResourceLoader
接口是资源查找定位策略的统一抽象,具体的资源查找定位策略则由相应的ResourceLoader
实现类给出。**我想,把ResourceLoader
称作统一资源定位器或许才更恰当一些吧!ResourceLoader
定义如下:
其中最主要的就是 Resource getResource(String location);
方法,通过它,我们就可以根据指定的资源位置,定位到具体的资源实例。
1. 可用的ResourceLoader
DefaultResourceLoader
ResourceLoader
有一个默认的实现类,即org.springframework.core.io.DefaultResourceLoader
,该类默认的资源查找处理逻辑如下。
(1)首先检查资源路径是否以classpath:
前缀打头,如果是,则尝试构造ClassPathResource
类型资源并返回。
(2)否则,(a)尝试通过URL,根据资源路径来定位资源,如果没有抛出MalformedURLException
,有则会构造UrlResource类型的资源并返回;(b)如果还是无法根据资源路径定位指定的资源,则委派getResourceByPath(String)
方法来定位,DefaultResourceLoader
的getResourceByPath(String)
方法默认实现逻辑是,构造ClassPathResource
类型的资源并返回。
如果最终没有找到符合条件的相应资源,getResourceByPath(String)
方法就会构造一个实际上并不存在的资源并返回。
FileSystemResourceLoader
为了避免DefaultResourceLoader
在最后getResourceByPath(String)
方法上的不恰当处理,我们可以使用org.springframework.core.io.FileSystemResourceLoader
,它继承自DefaultResourceLoader
,但覆写了getResourceByPath(String)
方法,使之从文件系统加载资源并以FileSystemResource
类型返回。这样,我们就可以取得预想的资源类型。
(原文这里给了DefaultResourceLoader和FileSystemResourceLoader的代码例子)
2. ResourcePatternResolver ——批量查找的ResourceLoader
ResourcePatternResolver
是ResourceLoader
的扩展,ResourceLoader
每次只能根据资源路径返回确定的单个Resource实例,而ResourcePatternResolver
则可以根据指定的资源路径匹配模式,每次返回多个Resource实例。
ResourcePatternResolver
最常用的一个实现是org.springframework.core.io.support.PathMatchingResourcePatternResolver
。
在构造PathMatchingResourcePatternResolver
实例的时候,可以指定一个ResourceLoader
,如果不指定的话,则PathMatchingResourcePatternResolver
内部会默认构造一个DefaultResourceLoader
实例。PathMatchingResourcePatternResolver
内部会将匹配后确定的资源路径,委派给它的ResourceLoader
来查找和定位资源。这样,如果不指定任何ResourceLoader
的话,PathMatchingResourcePatternResolver
在加载资源的行为上会与DefaultResourceLoader
基本相同,只存在返回的Resource数量上的差异。
(这里不做具体介绍,想了解的话可以看原文)
3. 回顾与展望
现在我们应该对Spring的统一资源加载策略有了一个整体上的认识,就如图5-1所示。
虽然现在看来比较“单薄”,不过,稍后,我们就会发现情况并非如此了。