Spring IoC容器ApplicationContext

本文深入探讨Spring框架中资源加载机制,包括ApplicationContext的主要实现类、Resource及其加载策略,以及如何利用ResourceLoader进行资源查找。同时,介绍了Spring的MessageSource接口及其实现,用于处理国际化消息,提供了一种简洁高效的消息资源管理方式。

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默认的实现类。

默认资源查找逻辑是:

  1. 以*classpath:*为前缀则返回ClassPathResource
  2. 尝试构造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或者ResourcePatternResolverApplicationContext有以下作用:

扮演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将委派容器中一个名称为messageSourceMessageSource接口实现来完成MessageSource 应该完成的职责,故应作如下配置:

    //
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>messages</value>
                <value>errorcodes</value>
            </list>
        </property>
    </bean>
复制代码
可用的MessageSource

  1. StaticMessageSource可硬编码添加信息,多用于测试
  2. ResourceBundleMessageSource常用生产环境
  3. 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>
复制代码

容器内部事件发布

自定义事件发布

转载于:https://juejin.im/post/5c63df1fe51d457fa074ac47

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值