Spring IOC官方文档学习笔记(十四)之ApplicationContext的其他功能

1.使用MessageSource

(1) 有时,我们的项目可能会面临国际化需求,例如:对不同国家的人,我们需返回不同语言的消息,而java本身已经给我们提供了ResourceBundle类实现国际化的需求,如下

//在resources目录下,新建两个配置文件,分别为message_en_us.properties和message_zh_cn.properties,内容如下

//message_en_us.properties文件中配置如下
country=us

//message_zh_cn.properties文件中配置如下,注意对中文使用unicode编码
country=\u4e2d\u56fd

//现在,我们希望我们的项目在不同的国家返回不同的country信息,那么就可以使用ResourceBundle类了,如下
public static void main(String[] args) {
    //使用ResourceBundle加载的文件都必须放置在resources根目录下,因此我们的message_en_us.properties和message_zh_cn.properties文件都位于resources根目录,而且这些文件都必须按照${name}_${language}_${region}的方式来命名,因为这种命名方式正好能对应ResourceBundle.getBundle()方法中的参数,例如ResourceBundle.getBundle("message", new Locale("zh", "cn")),其中,message对应${name},zh对应${language},cn对应${region},即ResourceBundle.getBundle("message", new Locale("zh", "cn"))这个方法会读取我们的message_zh_cn.properties配置文件,这样我们就可以根据不同的参数来读取不同的文件,达到国际化的目的

    //未指定它的Locale,因此java获取它当前所在的地区,为cn
    ResourceBundle DefaultBundle = ResourceBundle.getBundle("message");
    System.out.println(DefaultBundle.getString("country"));

    //指定地区为cn
    ResourceBundle cnBundle = ResourceBundle.getBundle("message", new Locale("zh","cn"));
    System.out.println(cnBundle.getString("country"));

    //指定地区为us
    ResourceBundle uSbundle = ResourceBundle.getBundle("message", new Locale("en","us"));
    System.out.println(uSbundle.getString("country"));
}

//打印结果如下,通过ResourceBundle实现了国际化
中国
中国
us

(2) Spring提供了MessageSource来帮助我们实现国际化功能,具体的使用方法同jdk中的ResourceBundle,如下

//在resources目录下,新建两个配置文件,分别为message_en.properties和message_zh.properties,内容如下

//message_en.properties文件中配置如下
country=us

//message_zh.properties文件中配置如下
country=中国

<!-- 配置xml -->
<beans ....>
        <!-- 使用ReloadableResourceBundleMessageSource类,向容器中注入MessageSource用于国际化功能,注意:这个bean的名称必须为messageSource -->
        <bean id="messageSource"
              class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
            <property name="basenames">
                <list>
                    <!-- 指定消息源为message,那么spring会去寻找名称中包含message的配置文件 -->
                    <value>message</value>
                </list>
            </property>
            <!-- 避免中文乱码 -->
            <property name="defaultEncoding">
                <value>UTF-8</value>
            </property>
        </bean>
</beans>

//ApplicationContext继承了MessageSource接口
MessageSource messageSource = new ClassPathXmlApplicationContext("beans.xml");
//指定不同的语言,来获取不同消息
String zh = messageSource.getMessage("country", null, Locale.CHINESE);
System.out.println(zh);

String en = messageSource.getMessage("country", null, Locale.ENGLISH);
System.out.println(en);

//启动容器,输出如下
中国
us

(3) Spring的MessageSource还提供了占位符功能,来进行消息内容的填充,如下例所示

//向message_zh.properties中添加配置项如下,{0}表示第一个占位符,还有{1},{2}等等,以此类推
argument=we need {0}

//main函数
MessageSource messageSource = new ClassPathXmlApplicationContext("beans.xml");
                                                     //Object[]指定向占位符填充的内容
String argument = messageSource.getMessage("argument", new Object[]{"蛋糕"}, Locale.CHINESE);
System.out.println(argument);

//启动后,打印如下
we need 蛋糕

2.标准和自定义事件

(1) Spring中的事件是通过ApplicationEvent类和ApplicationListener接口提供的,如果一个bean实现了ApplicationListener接口,那么每当一个ApplicationEvent发布到Spring中时,都会通知该bean

(2) Spring中内置事件

事件 说明
ContextRefreshedEvent 容器初始化或刷新时(refresh)时发布该事件
ContextStartedEvent 通过调用ConfigurationApplicationContext接口中的start()方法启动容器时发布该事件
ContextStoppedEvent 通过调用ConfigurationApplicationContext接口中的stop()方法停止容器时发布该事件
ContextClosedEvent 通过调用ConfigurationApplicationContext接口中的close()方法或jvm关闭钩子关闭容器时发布该事件
RequestHandledEvent 适用于使用了DispatcherServlet的web环境中,在请求完成后发布该事件,用于告知所有的bean已经为http请求提供了服务
ServletRequestHandledEvent RequestHandledEvent的子类,其中添加了Servlet特定信息

(3) 示例如下

//现在假设有一个用户注册事件,每当一个用户注册后,进行相应的其他操作(如发送邮件等等)
//自定义事件,需继承ApplicationEvent
public class RegisterEvent extends ApplicationEvent {
    private String username;

    public RegisterEvent(Object source,String username) {
        super(source);
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

//使用ApplicationEventPublisher中的publishEvent()方法来向容器中发布一个事件
@Service
public class RegisterService implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher publisher;

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

    //publishEvent()方法会阻塞,直到所有的监听器都完成了对事件的处理
    public void finishRegister(String username) {
        this.publisher.publishEvent(new RegisterEvent(this, username));
    }
}

//实现ApplicationListener接口,实现某种类型事件的监听者
@Component
public class RegisterLister implements ApplicationListener<RegisterEvent> {

    //每当有一个RegisterEvent事件发布后,都会触发该回调
    @Override
    public void onApplicationEvent(RegisterEvent registerEvent) {
        System.out.println("用户:" + registerEvent.getUsername() + "完成注册...");
        //do other things,such as send emails
    }
}


////除了上面实现ApplicationListener接口外,我们还可以使用@EventListener注解来定义监听器,如下
//@Component
//public class RegisterLister {
//    //每当有一个RegisterEvent事件时,都会回调该方法,跟上面的例子等价,方法签名指定了监听事件的类型
//    @EventListener
//    public void handleRegister(RegisterEvent registerEvent) {
//        System.out.println("用户:" + registerEvent.getUsername() + "完成注册...");
//    }
//}

//main
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext("cn.example.spring");
String username ="zpc";
ctx.getBean(RegisterService.class).finishRegister(username);

//启动后,容器打印如下,其中Spring使用了观察者模式,来实现了一个事件发布与订阅的功能
用户:zpc完成注册...

(4) Spring还支持在处理完一个事件后发布一个新事件,如下

//每处理完RegisterEvent事件后都会发布一个新事件FinishRegisterEvent,如果需要发布多个事件,可以返回一个Collection事件集合
@EventListener
public FinishRegisterEvent handleRegister(RegisterEvent registerEvent) {
     //...
}

(5) 使用@Async注解,可使监听器异步处理该事件,如下

//注意:异步事件所抛出的异常会被AsyncUncaughtExceptionHandler捕获处理,不会传播到调用方,同时异步事件不支持像上面的例子那样通过方法返回值来在处理完一个事件后发布一个新事件
@EventListener
@Async
public void handleRegister(RegisterEvent registerEvent) {
  //在另一个线程中进行处理
}

(6) 使用@Order注解,可以指定不同监听器的执行顺序

@EventListener
@Order(42)
public void handleRegister(RegisterEvent registerEvent) {

}

3.使用Resource来便捷的访问资源

(1) Spring提供了Resource,ResourceLoader相关接口,来从类路径,文件系统中或URL中等来获取资源

热门相关:超武穿梭   苏醒的秘密   仗剑高歌   第一神算:纨绔大小姐   刺客之王