ApplicationContext的额外功能

org.springframework.context包增加了ApplicationContext接口,它继承了BeanFactory接口,除了以面向应用框架的风格扩展接口来提供一些额外的功能。很多人以完全声明的方式使用ApplicationContext,甚至没有以编程的方式去创建它,而是依赖诸如ContextLoader等支持类来自动的实例化ApplicationContext,作为Java EE web应用程序正常启动的一部分。 为了增强BeanFactory在面向框架风格的功能,上下文的包还提供了以下的功能:

  • 通过MessageSource接口访问i18n风格的消息
  • 通过ResourceLoader接口访问类似URL和文件资源
  • 通过ApplicationEventPublisher接口,即bean实现ApplicationListener接口来进行事件发布
  • 通过HierarchicalBeanFactory接口实现加载多个(分层)上下文,允许每个上下文只关注特定的层,例如应用中的web层

通过MessageSource接口访问i18n风格的消息

国际化在于对特定地区的访问提供属于特定地区的语言反馈。

ApplicationContext接口继承了一个叫做MessageSource的接口,因此它也提供了国际化(i18n)的功能。Spring也提供了HierarchicalMessageSource接口,它可以分层去解析信息。

  • String getMessage(String code, Object[] args, String default, Locale loc): 这个基础的方法用来从MessageSource检索消息。当指定的区域中没有发现消息时,将使用默认的。任何参数传递都将使用标准库提供的MessageFormat变成替换值。
  • String getMessage(String code, Object[] args, Locale loc): 本质上和前面提供的方法相同,只有一个区别,就是当没有指定消息,又没有发现消息,将会抛出NoSuchMessageException 异常。
  • String getMessage(MessageSourceResolvable resolvable, Locale locale): 所有的属性处理方法都被包装在一个名为MessageSourceResolvable的类中,你可以使用此方法。

Spring提供了ResourceBundleMessageSource和StaticMessageSource两个MessageSource实现。它们两个都实现了HierarchicalMessageSource以便处理嵌套消息。StaticMessageSource很少使用,但是它提供了通过编程的方式增加消息源。

    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>message.format</value>
            </list>
        </property>
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>

    <bean id="messageUtils" class="com.example.springdemo.utils.MessageUtils">
        <property name="messageSource" ref="messageSource"/>
    </bean>
复制代码
public class MessageUtils {

    private MessageSource messageSource;

    public MessageSource getMessageSource() {
        return messageSource;
    }

    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    public String getMessage(String temp, Object[] message, Locale locale) {
        return messageSource.getMessage(temp, message, "Required", locale);
    }

}
复制代码
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/spring/message.xml");
        String temp = "argument.required";
        Object[] objects = new Object[] {
                "Java Coder!"
        };
        MessageUtils messageUtils = context.getBean(MessageUtils.class);
        System.out.println(messageUtils.getMessage(temp, objects, Locale.SIMPLIFIED_CHINESE));
    }
复制代码

资源文件如下:

通过_en_CN这种格式的命名,可以使得MessageSource通过Locale来解析获取到对应的语言的资源文件。最终可以根据传入的语言信息来返回对应的消息。

标准和自定义事件

ApplicationEvent类和ApplicationListener接口提供了ApplicationContext中的事件处理。

如果一个bean实现了ApplicationListener接口,然后它被部署到上下问中,那么每次ApplicationEvent发布到ApplicationContext中时,bean都会收到通知。本质上,这是观察者模型。

我们可以自定义自己的事件:

public class BlackListEvent extends ApplicationEvent {

    private String address;

    private String test;

    public BlackListEvent(Object source, String address, String test) {
        super(source);
        this.address = address;
        this.test = test;
    }

    @Override
    public String toString() {
        return "{address:" + address +",text:" + test + "}";
    }

}
复制代码

为了发布一个自定义的ApplicationEvent,在ApplicationEventPublisher中调用publishEvent()方法。通常在实现了ApplicationEventPublisherAware接口并把它注册为一个Spring bean的时候它就完成了。下面的例子展示了这么一个类:

public class EmailService implements ApplicationEventPublisherAware {

    //黑名单
    private List<String> blackLists;

    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void setBlackLists(List<String> blackLists) {
        this.blackLists = blackLists;
    }

    public void sendEmail(String address, String text) {
        if (blackLists.contains(address)) {
            BlackListEvent event = new BlackListEvent(this, address, text);
            publisher.publishEvent(event);
            return;
        }
        //send email
    }

}
复制代码

在配置时,Spring容器将检测到EmailService实现了ApplicationEventPublisherAware,并将自动调用setApplicationEventPublisher()方法。实际上,传入的参数将是Spring容器本身;您只需通过ApplicationEventPublisher接口与应用程序上下文进行交互。

在实际情况中可以直接使用@Service和@Autowired注解从Ioc容器中获取到容器对象,而不需要实现ApplicationEventPublisherAware类。

这两种我比较倾向于对ApplicationEventPublisherAware的实现,因为事件通常用于处理通用的业务,比如消息推送或者邮件发送,这种功能和系统的主要功能没多大关系,因此可以将他们当成组件放置在别的packet下,降低耦合。

对事件的监听有两种实现方式:

  1. 实现ApplicationListener<T>接口
  2. 采用@EventListener注解

其中2方法更加快捷方便,是spring4.2所引入的新功能。相比起1。2方法可以将复数的监听事件统一放置在一个类下面,方便管理。

实现ApplicationListener<T>接口
public class BlackListNotifier implements ApplicationListener<BlackListEvent> {
    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @Override
    public void onApplicationEvent(BlackListEvent blackListEvent) {
        System.out.println("黑名单:" + blackListEvent);
        System.out.println("通知目标用户:" + notificationAddress);
    }
}
复制代码
采用@EventListener注解
public class BlackListNotifier {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @EventListener
    public void onApplicationEvent(BlackListEvent blackListEvent) {
        System.out.println("黑名单:" + blackListEvent);
        System.out.println("通知目标用户:" + notificationAddress);
    }

}
复制代码

这两种方法都可以实现监听,但是我在测试的时候发现一个问题:

当我采用xml实例化bean的时候:

<beans>
    <bean id="blackListNotifier" class="com.example.springdemo.listener.BlackListNotifier">
        <property name="notificationAddress" value="blacklist@example.org" />
    </bean>

    <bean id="emailService" class="com.example.springdemo.service.impl.EmailService">
        <property name="blackLists">
            <list>
                <value>known.spammer@example.org</value>
                <value>known.hacker@example.org</value>
                <value>john.doe@example.org</value>
            </list>
        </property>
    </bean>

</beans>
复制代码

方法2无法获取到监听事件。

当我将它修改成Java类配置后就可以了:

@Configuration
public class EventConf {

    @Bean
    public BlackListNotifier blackListNotifier() {
        BlackListNotifier blackListNotifier = new BlackListNotifier();
        blackListNotifier.setNotificationAddress("unkonw@163.com");
        return blackListNotifier;
    }

}
复制代码

这里文档上也没有提,希望有知道的大佬能够为我解答,感谢。

Spring ApplicationContext 作为Java EE RAR文件部署

可以将Spring ApplicationContext部署为RAR文件,将上下文和所有他所需的bean的类和JAR库封装在Java EE RAR部署单元中。这相当于独立启动一个ApplicationContext,它在Java EE环境中可以访问Java EE服务资源。RAR部署在一些没用头信息的war文件中更自然的选择,实际上,一个war文件在没有http入口的时候,那么它就仅仅是用来在Java EE环境中启动Spring ApplicationContext。

RAR部署在一些没用头信息的war文件中更自然的选择,实际上,一个war文件在没有http入口的时候,那么它就仅仅是用来在Java EE环境中启动Spring ApplicationContext。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值