ApplicationContext
主要实现类
其作用分别是:
- 在默认情况下,从文件系统加载bean定义以及相关资源的 ApplicationContext 实现。
- 在默认情况下,从Classpath加载bean定义以及相关资源的 ApplicationContext 实现。
- Spring提供的用于Web应用程序的 ApplicationContext 实现。
统一资源加载策略
Spring中的Resource
Resource
及其主要实现类:
接口定义源码如下:
public interface InputStreamSource { InputStream getInputStream() throws IOException;}复制代码
public interface Resource extends InputStreamSource { /** * Determine whether this resource actually exists in physical form. */ boolean exists(); /** * Indicate whether non-empty contents of this resource can be read via */ default boolean isReadable() { return exists(); } /** * Indicate whether this resource represents a handle with an open stream. */ default boolean isOpen() { return false; } /** * Determine whether this resource represents a file in a file system. */ default boolean isFile() { return false; } /** * Return a URL handle for this resource. */ URL getURL() throws IOException; /** * Return a URI handle for this resource. */ URI getURI() throws IOException; /** * Return a File handle for this resource. */ File getFile() throws IOException; /** * Return a { @link ReadableByteChannel}. */ default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); } /** * Determine the content length for this resource. */ long contentLength() throws IOException; /** * Determine the last-modified timestamp for this resource. */ long lastModified() throws IOException; /** * Create a resource relative to this resource. */ Resource createRelative(String relativePath) throws IOException; /** * Determine a filename for this resource, i.e. typically the last */ @Nullable String getFilename(); /** * Return a description for this resource, */ String getDescription();}复制代码
这些方法可以帮助我们查询资源状态、访问资源内容,甚至根据当前资源创建新的相对资源;
可以继承org.springframework.core.io.AbstractResource
抽象类实现具体资源类。
ResourceLoader,“更广义的URL”
如何去查找和定位这些资源,该是 ResourceLoader 的职责所在。
ResourceLoader
接口定义:
package org.springframework.core.io;public interface ResourceLoader { /** Pseudo URL prefix for loading from the class path: "classpath:". */ String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; /** * Return a Resource handle for the specified resource location. */ Resource getResource(String location); /** * Expose the ClassLoader used by this ResourceLoader. */ @Nullable ClassLoader getClassLoader();}复制代码
可用的 ResourceLoader
一、DefaultResourceLoader
ResourceLoader
默认的实现类。
默认资源查找逻辑是:
- 以*classpath:*为前缀则返回
ClassPathResource
; - 尝试构造
UrlResource
,无资源则抛出MalformedURLException
二、FileSystemResourceLoader
继承自DefaultResourceLoader
,但覆写了getResourceByPath(String)
方法,使之从文件系统加载资源并以FileSystemResource
类型返回。
ResourcePatternResolver——批量查找的 ResourceLoader
定义了Resource[] getResources(String locationPattern)
方法用于批量加载Resource
。
public interface ResourcePatternResolver extends ResourceLoader { /** * Pseudo URL prefix for all matching resources from the class path: "classpath*:" */ String CLASSPATH_ALL_URL_PREFIX = "classpath*:"; /** * Resolve the given location pattern into Resource objects. */ Resource[] getResources(String locationPattern) throws IOException;}复制代码
其主要实现类是PathMatchingResourcePatternResolver
。
PathMatchingResourcePatternResolver
该实现类支持ResourceLoader
级别的资源加载,支持基于Ant
风格的路径匹配模式(类似于 **/*.suffix之类的路径形式),支持ResourcePatternResolver
新增加的classpath*:
前缀等,基本上集所有技能于一身。
其构造方法如下:
public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { /** * Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader. */ public PathMatchingResourcePatternResolver() { this.resourceLoader = new DefaultResourceLoader(); } /** * Create a new PathMatchingResourcePatternResolver. */ public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) { Assert.notNull(resourceLoader, "ResourceLoader must not be null"); this.resourceLoader = resourceLoader; } /** * Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader. */ public PathMatchingResourcePatternResolver(@Nullable ClassLoader classLoader) { this.resourceLoader = new DefaultResourceLoader(classLoader); }复制代码
其通过调用传入的或默认的ResourceLoader
加载资源,故加载资源策略与其持有的ResourceLoader
一致。
ApplicationContext 与 ResourceLoader
AbstractApplicationContext
的子类的资源加载完全依托于内部持有的PathMatchingResourcePatternResolver
来实现。
作为ResourceLoader
或者ResourcePatternResolver
的ApplicationContext
有以下作用:
扮演ResourceLoader
的角色
如下代码:
ResourceLoader resourceLoader = new ClassPathXmlApplicationContext("配置文件路径");// 或者 配置文件路径"); // ResourceLoader resourceLoader = new FileSystemXmlApplicationContext(" Resource fileResource = resourceLoader.getResource("D:/spring21site/README"); assertTrue(fileResource instanceof ClassPathResource); assertFalse(fileResource.exists()); Resource urlResource2 = resourceLoader.getResource("http://www.spring21.cn"); assertTrue(urlResource2 instanceof UrlResource);复制代码
ResourceLoader
类型的注入
通过对需要注入的bean实现ApplicationContextAware
或者ResourceLoaderAware
来依赖SpringAPI自动注入对应的对象,并且两者都使用ResourceLoader
类型持有对象(不绝对)。
Resource
类型的注入
如下代码:
public class XMailer { private Resource template; public Resource getTemplate() { return template; } public void setTemplate(Resource template) { this.template = template; }}复制代码
如下配置:
复制代码 ...
即可成功注入Resource类型对象。
其原理是: ApplicationContext 启动伊始,会通过一个org.springframework.beans.support.ResourceEditorRegistrar 来注册 Spring提供的针对Resource类型的PropertyEditor实现到容器中,这个PropertyEditor 叫做org.springframework.core.io.ResourceEditor 。这样,ApplicationContext 就可以正确地识别 Resource类型的依赖了。至于ResourceEditor怎么实现我就不用说了吧?你想啊,把配置文件中的路径让 ApplicationContext 作为 ResourceLoader给你定位一下不就得了。
I18n MessageSource
JavaSE的Locale与ResourceBundle
java.util.Locale
代表不同国家和地区,作用等同枚举。
Locale
的三个构造方法:
Locale(String language)Locale(String language, String country)Locale(String language, String country, String variant)复制代码
java.util.ResourceBundle
用于保存多个Locale
对应的资源,通常是properties
保存的键值对。通过如下静态方法获取对应的ResourceBundle
实例,进而获取对应参数。
public static final ResourceBundle getBundle(String baseName,Locale locale)复制代码
MessageSource与ApplicationContext
org.springframework.context.MessageSource
接口定义如下:
public interface MessageSource { /** * Try to resolve the message. Return default message if no message was found. */ @Nullable String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale); /** * Try to resolve the message. Treat as an error if the message can't be found. */ String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException; /** * Try to resolve the message using all the attributes contained within the * { @code MessageSourceResolvable} argument that was passed in. */ String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;}复制代码
相对java原生,省略了获取ResourceBundle
这一步骤。
实现了MessageSource的ApplicationContext
ApplicationContext
将委派容器中一个名称为messageSource
的MessageSource
接口实现来完成MessageSource
应该完成的职责,故应作如下配置:
//复制代码
messages errorcodes
可用的MessageSource
StaticMessageSource
可硬编码添加信息,多用于测试ResourceBundleMessageSource
常用生产环境ReloadableResourceBundleMessageSource
可定时更新资源
ApplicationContext
内的messageSource
三选一;或者可自己继承AbstractMessageSource
实现定制化。
MessageSourceAware 和 MessageSource 的注入
实现MessageSourceAware
接口,然后注册到ApplicationContext
容器的bean
将被注入MessageSource
。
也可直接通过XML配置注入:
messages errorcodes 复制代码