ApplicationContext提供的其它能力
本篇文章主要介绍一下ApplicationContext的其它能力。Environment
,PropertySource
,MessageSource
,Event
,ResourceLoader
#
Environment主要是为我们容器提供一个执行环境,可以控制哪些bean能够在哪些环境下(profiles
)实例化,并包含对应的属性
信息,这个特别适用于我们不同环境不同配置的使用场景。Spring在初始化容器时会默认创建Environment
实例并注入进去,这里就用AnnotationConfigApplicationContext
举例说明。当我们执行以下方法创建容器后
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(APP.class);
Spring默认会创建StandardEnvironment
对象注入到AbstractApplicationContext.environment
属性中。我們可以通过Environment
对象来获取当前的ActiveProfiles
,而且Environment
接口同时也继承了PropertyResolver接口,所以我们也可以通过其对象来获取当前属性
信息
那么在spring bean中我们可以用下面方式来获取
#
使用@Autowired 自动装配@AutowiredEnvironment environment;
#
使用实现EnvironmentAware接口EnvironmentAware能够装配的原理是因为ApplicationContextAwareProcessor
(实现了BeanPostProcessor
接口)在其invokeAwareInterfaces()
方法中执行了实例的setEnvironment
方法
@Componentpublic class MyEnvironmentAware implements EnvironmentAware { private Environment environment; @Override public void setEnvironment(Environment environment) { this.environment = environment; }}
#
PropertySource表示 key/value 属性对 的抽象类。底层源对象可以是封装属性的任何T类型。如:java.util.Properties,java.util.Map,ServletContext和ServletConfig对象。在Spring中默认有很多PropertySource实现类,如在创建StandardEnvironment对象时会默认使用MutablePropertySources
(可以说是管理PropertySource集合对象)来添加PropertiesPropertySource
和SystemEnvironmentPropertySource
对象。下面这张图是Environment
和Propertysource
的类关系图。
#
MessageSource用于解析消息的接口,支持消息的参数化和格式化。
Spring默认提供了多种实现,如ResourceBundleMessageSource
,ReloadableResourceBundleMessageSource
,DelegatingMessageSource
。
默认情况下(我们没有手动配置/注册MessageSource对象)Spring在AbstractApplicationContext.initMessageSource
中创建一个DelegatingMessageSource
对象,他只是一个空的消息对象。如果我们需要提供国际化或者配置参数化消息,我们需要配置ReloadableResourceBundleMessageSource
,如下:
@Bean public ReloadableResourceBundleMessageSource messageSource(){ ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource(); //这里i18n名字和配置文件一样 reloadableResourceBundleMessageSource.setBasenames("i18n"); reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8"); return reloadableResourceBundleMessageSource; }
ReloadableResourceBundleMessageSource默认会找我们setBasenames配置的文件并加载,这里我们可以按不同的语言以i18n_{Locale}
创建我们需要国际化的配置,如:i18n_en.properties
,i18n_zh.properties
,i18n_zh_CN.properties
等
#
i18n.properties默认配置,如果没有找到准备国际化配置,默认取此配置数据
404=页面未找到Default
#
i18n_ch.properties404=页面未找到
#
i18n_en.properties404=page not found
同样我们可以用@Autowire
和MessageSourceAware
方便获取MessageSource,不仅如此我们还可以直接用ApplicationContext.getMessage
解析消息
#
EventSpring默认为我们实现了一套发布/订阅机制,我们首先需要了解的ApplicationEventMulticaster
接口:可以管理多个ApplicationListener
对象并向其发布事件的接口。Spring 在AbstractApplicationContext.initApplicationEventMulticaster
中初始化applicationEventMulticaster
(事件处理器),在此之前我们可以自己创建一个ApplicationEventMulticaster
的实现对象(因为可以设置taskExecutor
-异步处理,errorHandler
-错误处理机制)来覆盖Spring默认为我们创建的SimpleApplicationEventMulticaster
对象。
#
事件发布#
创建事件我们发布的事件需要继承ApplicationEvent
。
public class MyApplicationEvent extends ApplicationEvent { public MyApplicationEvent(Object source){ super(source); }}
#
发布事件发布事件需要获取事件发布器ApplicationEventPublisher
,applicationEventPublisher通过publishEvent
方法向applicationEventMulticaster发布ApplicationEvent
消息。这里提供两种方法。第一种就是实现ApplicationEventPublisherAware
接口,第一种就是获取到ApplicationContext
对象,因为ApplicationContext
接口也是继承了ApplicationEventPublisher
。以下列举实现ApplicationEventPublisherAware接口
@Componentpublic class MyApplicationEventPublisher implements ApplicationEventPublisherAware { ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; }}
#
事件订阅订阅事件需要实现ApplicationListener
接口,Spring利用ApplicationListenerDetector
后置处理器向applicationEventMulticaster
添加监听器,实现事件的订阅。
@Componentpublic class MyApplicationListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { System.out.println(event.getClass()); }}
值得说明的是这里的MyApplicationListener
是订阅了所有ApplicationEvent
消息,其实我们也可以利用泛型指定订阅消息
@Componentpublic class MyApplicationListener implements ApplicationListener<MyApplicationEvent> { @Override public void onApplicationEvent(MyApplicationEvent event) { System.out.println(event.getClass()); }}
#
ResourceLoader用于加载资源Resource
的接口。
Resource 接口旨在提供更加强大的功能用于抽象访问低级资源
Spring 容器默认提供了API便于内部或者使用人员方便访问资源,资源包括classpath
,file
,https
等。ResourceLoader接口定义如下
public interface ResourceLoader { Resource getResource(String location); ClassLoader getClassLoader();}
从接口定义上我们可以知道,ResourceLoader
提供了getResource
方法访问资源文件,参数是资源路径。我们先来看Spring对ResourceLoader实现类图
从图中我们可以看到ApplicationContext
是继承了ResourceLoader
接口,也就是说在Spring容器里ApplicationContext
对象也是有getResource
能力的。不仅如此,我们的ApplicaitonContext还继承了ResourcePatterResolver
接口,意思是还可以通过通配符
加载多个资源。因此当我们有加载资源的需求时我们可以通过ApplicationContext
对象(实现ApplicationContextAware
接口)或者ResourceLoader
对象(实现ResourceLoaderAware
接口)来获取资源文件。
以下都是有效资源路径(不是全部)
classpath:com/myapp/config.xml classpath路径
- classpath*:com/myapp/config.xml
- classpath:com/myapp/.xml
- classpath*:com/**/config.xml
file:///data/config.xml 文件系统路径
- file:///data/*.xml
http[s]://myserver/logo.png 网络路径
/data/config.xml 依赖于当前ApplicationContext
- /data/*.xml
注意:我们在仔细看看上面的类图
DefaultResourceLoader
和PathMatchingResourcePatternResolver
他们分别是对ResourceLoader
和ResourcePatternResolver
实现,记住他们是可以脱离容器独立使用的。下面是举例
#
DefaultResourceLoader 使用DefaultResourceLoader defaultResourceLoader = new DefaultResourceLoader();Resource resource = defaultResourceLoader.getResource("classpath:a.properties");
#
PathMatchingResourcePatternResolver 使用PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();Resource[] resources = resourcePatternResolver.getResources("file:/Users/lykos/demo/*.properties");for(Resource r : resources){ Properties properties = new Properties(); PropertiesLoaderUtils.fillProperties(properties, new EncodedResource(r,"utf-8"));}