Spring 系列篇之ApplicationContext提供的其它能力

本篇文章主要介绍一下ApplicationContext的其它能力。Environment,PropertySourceMessageSource,Event,ResourceLoader
[图片上传中...(image.png-46c0e-1589068396570-0)]

Environment

主要是为我们容器提供一个执行环境,可以控制哪些bean能够在哪些环境下(profiles)实例化,并包含对应的属性信息,这个特别适用于我们不同环境不同配置的使用场景。Spring在初始化容器时会默认创建Environment实例并注入进去,这里就用AnnotationConfigApplicationContext举例说明。当我们执行以下方法创建容器后

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(APP.class);

Spring默认会创建StandardEnvironment对象注入到AbstractApplicationContext.environment属性中。我們可以通过Environment对象来获取当前的ActiveProfiles,而且Environment接口同时也继承了PropertyResolver接口,所以我们也可以通过其对象来获取当前属性信息
那么在spring bean中我们可以用下面方式来获取

使用@Autowired 自动装配

@Autowired
Environment environment;

使用实现EnvironmentAware接口

EnvironmentAware能够装配的原理是因为ApplicationContextAwareProcessor(实现了BeanPostProcessor接口)在其invokeAwareInterfaces()方法中执行了实例的setEnvironment方法

@Component
public 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集合对象)来添加PropertiesPropertySourceSystemEnvironmentPropertySource对象。下面这张图是EnvironmentPropertysource的类关系图。

Environment-PropertySource关系图

ApplicationContext 其它功能

MessageSource

用于解析消息的接口,支持消息的参数化和格式化。


MessageSource类图

MessageSourceResolvable & 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.properties

404=页面未找到

i18n_en.properties

404=page not found

同样我们可以用@AutowireMessageSourceAware方便获取MessageSource,不仅如此我们还可以直接用ApplicationContext.getMessage解析消息

Event

Spring默认为我们实现了一套发布/订阅机制,我们首先需要了解的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接口

@Component
public class MyApplicationEventPublisher implements ApplicationEventPublisherAware {
    ApplicationEventPublisher applicationEventPublisher;
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

事件订阅

订阅事件需要实现ApplicationListener接口,Spring利用ApplicationListenerDetector后置处理器向applicationEventMulticaster添加监听器,实现事件的订阅。

@Component
public class MyApplicationListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println(event.getClass());
    }
}

值得说明的是这里的MyApplicationListener是订阅了所有ApplicationEvent消息,其实我们也可以利用泛型指定订阅消息

@Component
public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
    @Override
    public void onApplicationEvent(MyApplicationEvent event) {
        System.out.println(event.getClass());
    }
}
Spring event类图

ResourceLoader

用于加载资源Resource的接口。

Resource 接口旨在提供更加强大的功能用于抽象访问低级资源

Spring 容器默认提供了API便于内部或者使用人员方便访问资源,资源包括classpath,file,https等。ResourceLoader接口定义如下

public interface ResourceLoader {
    Resource getResource(String location);
    ClassLoader getClassLoader();
}

从接口定义上我们可以知道,ResourceLoader提供了getResource方法访问资源文件,参数是资源路径。我们先来看Spring对ResourceLoader实现类图

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

注意:我们在仔细看看上面的类图DefaultResourceLoaderPathMatchingResourcePatternResolver他们分别是对ResourceLoaderResourcePatternResolver实现,记住他们是可以脱离容器独立使用的。下面是举例

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"));
}

感谢

感谢各位老铁花时间观看!
欢迎留言指正!
内容持续更新!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354