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
    }
}

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

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