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;
}
}
复制代码
如下配置:
<bean id="mailer" class="...XMailer">
<property name="template" value="..resources.default_template.vm"/>
...
</bean>
复制代码
即可成功注入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
应该完成的职责,故应作如下配置:
//
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages</value>
<value>errorcodes</value>
</list>
</property>
</bean>
复制代码
可用的MessageSource
StaticMessageSource
可硬编码添加信息,多用于测试ResourceBundleMessageSource
常用生产环境ReloadableResourceBundleMessageSource
可定时更新资源
ApplicationContext
内的messageSource
三选一;或者可自己继承AbstractMessageSource
实现定制化。
MessageSourceAware 和 MessageSource 的注入
实现MessageSourceAware
接口,然后注册到ApplicationContext
容器的bean
将被注入MessageSource
。
也可直接通过XML配置注入:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages</value>
<value>errorcodes</value>
</list>
</property>
</bean>
<!--Validator将配setter注入ResourceBundleMessageSource-->
<bean id="validator" class="...Validator">
<property name="messageSource" ref="messageSource"/>
</bean>
复制代码