Spring基本用法2——使用Spring容器(ApplicationContext)
前言:Spring有两个核心接口(BeanFactory和ApplicationContext),其中ApplicationContext是BeanFactory的子接口,它们都可以代表Spring容器,而Spring容器就是生成Bean实例的工厂,并管理容器中的Bean。而ApplicationContext作为功能更强大的Spring容器,提供了诸如资源访问(URL和文件)、事件机制、同时加载多个配置文件、国际化支持等扩展功能。因此,本文介绍在项目中常用到的功能模块
本篇文章重点关注以下问题:
- ApplicationContext的国际化支持
- ApplicationContext的事件机制
- 在Bean中获取Spring容器
1. ApplicationContext的国际化支持
ApplicationContext接口继承了MessageSource接口,因此具备了国际化功能。下面是MessageSource接口中定义的三个用于国际化的方法:
public interface MessageSource { /** * 尝试解析国际化信息,如果为找到相关国际化配置,则返回默认信息 * @param code 待国际化信息在配置文件中的关键字 * @param args 待国际化信息中占位符的值 * @param defaultMessage 查找国家化配置失败后的默认返回值 * @param locale Locale信息 * @return */ String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale); /** * 尝试解析国际化信息,如果为找到相关国际化配置,则返回null * @param code 待国际化信息在配置文件中的关键字 * @param args 待国际化信息中占位符的值 * @param locale Locale信息 * @return */ String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException; /** * 以国际化解析器进行国际化 * @param resolvable 国际化解析接口 * @param locale Locale信息 * @return */ String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException; }
1.1 在Spring中配置MessageSource的Bean:通常使用ResourceBundleMessageSource类
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <!-- 驱动Spring调用messageSource Bean的setBasenames()方法,该方法需要一个数组参数,使用list元素配置多个数组元素 --> <property name="basenames"> <list> <value>com/wj/chapter2/applicationContextg/i18/message</value> <!-- 如果有多个资源文件,全部列在此处 --> </list> </property> </bean> </beans>上面配置文件中的粗体字只指定了一份国际化资源文件,其baseName是message,然后给出它的两份资源文件:message_en_US.properties、message_zh_CN.properties。
1.2 生成资源文件(中式、美式)
首先是美式资源文件:message_en_US.properties
hello=welcome,{0} now=now is :{0}
hello=欢迎你,{0} now=现在时间是 :{0}
因为资源文件中含有简体中文,所以需要用native2ascii工具将这份资源文件国际化(native2ascii 源文件 目标文件),得到的文件内容如下:
hello=\u6b22\u8fce\u4f60\uff0c{0} now=\u73b0\u5728\u65f6\u95f4\u662f\uff1a{0}
至此,程序就拥有了两份资源文件,可用于自适应美式英语和简体中文环境。
1.3 编写国际化功能测试代码
public class I18Main { // 1.指明xml配置文件位置,便于Spring读取,从而知道Bean的相关信息 private static final String PATH_XML = "com/wj/chapter2/applicationContextg/i18/applicationContext.xml"; @SuppressWarnings("resource") public static void main(String[] args)throws Exception { // 2.根据xml配置文件,创建Spring IOC容器的上下文 ApplicationContext ctx = new ClassPathXmlApplicationContext(PATH_XML); // 3.使用getMessage()方法获取本地化消息。(Locale的getDefault方法返回计算机环境的默认Locale) String hello = ctx.getMessage("hello" , new String[]{"熊燕子"}, Locale.getDefault(Locale.Category.FORMAT)); String now = ctx.getMessage("now" , new Object[]{new Date()}, Locale.getDefault(Locale.Category.FORMAT)); // 打印出两条本地化消息 System.out.println(hello); System.out.println(now); } }
1.4 查看运行结果
2. ApplicationContext的事件机制
ApplicationContext的事件机制是典型的观察者设计模式,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext的事件处理。如果容器中有一个ApplicationListener的Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener的Bean都将自动触发。
Spring的事件框架有如下两个重要成员:
- ApplicationEvent:容器事件,必须由ApplicationContext发布。
- ApplicationListener:监听器,可由容器中的任何监听器Bean担任。(必须实现ApplicationListener接口)
2.1 首先定义一个Spring容器事件
/** * 定义容器事件,此事件必须由容器ApplicationContext发布 * @author Administrator */ public class EmailEvent extends ApplicationEvent { private static final long serialVersionUID = 1259505481443188920L; private String address; private String text; public EmailEvent(Object source) { super(source); } // 初始化全部成员变量的构造器 public EmailEvent(Object source , String address , String text) { super(source); this.address = address; this.text = text; } // setter|getter public String getAddress() { return address; } public String getText() { return text; } public void setAddress(String address) { this.address = address; } public void setText(String text) { this.text = text; } }上面的EmailEvent类继承了ApplicationEvent,则该对象就可作为Spring容器的容器事件,便可通过容器发布。
2.2 实现容器事件的监听类
容器事件的监听器必须实现ApplicationListener接口,并实现接口中的唯一方法:
@FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * 每当容器内发生任何事件,此方法都会被触发. * @param event the event to respond to */ void onApplicationEvent(E event); }
EmailEvent事件对应的监听器如下:
/** * 定义监听器,当事件发生时,通知所有观察者 */ public class EmaiListener implements ApplicationListener<EmailEvent> { // 该方法会在容器发生事件时自动触发 @Override public void onApplicationEvent(EmailEvent evt) { System.out.println("需要发送邮件的接收地址: " + evt.getAddress()); System.out.println("需要发送邮件的邮件正文: " + evt.getText()); } }
2.3 补充: 定义一个全局的事件监听器
/** * 通过此监听器可观察所有在Spring容器中定义的事件(泛型指定顶层接口) */ public class AllEventListener implements ApplicationListener<ApplicationEvent> { // 该方法会在容器发生事件时自动触发 @Override public void onApplicationEvent(ApplicationEvent evt) { System.out.println("ApplicationEvent : " + evt); } }
利用泛型指定顶层ApplicationEvent类,关注所有发生的容器事件。
2.4 将监听器配置到xml配置文件中,以便容器知晓所有观察者(监听器)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <!-- 配置监听器(在Spring事件机制中,只需配置监听器即可) --> <bean class="com.wj.chapter2.applicationContextg.event.EmaiListener"/> <bean class="com.wj.chapter2.applicationContextg.event.AllEventListener"/> </beans>
从上面粗体字可看出,为Spring容器注册事件监听器,不需要像Swing编程那样采用代码进行编辑,只需在配置文件中简单说明即可。
2.5 通过ApplicationContext容器发布事件
public class EventTest { private static final String PATH_XML = "com/wj/chapter2/applicationContextg/event/applicationContext.xml"; public static void main(String[] args) { /** 1.触发ContextRefreshedEvent事件. */ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(PATH_XML); // 创建一个ApplicationEvent对象 EmailEvent emainEvent = new EmailEvent("test", "super_wj@163.com" , "this is a test"); /** 2.触发EmailEvent事件. */ ctx.publishEvent(emainEvent); /** 3.触发ContextClosedEvent事件. */ ctx.close(); } }
测试代码中会触发三个事件,两个容器自定义事件(ContextRefreshedEvent、ContextClosedEvent),一个用户自定义事件(EmailEvent)事件。
2.6 观察测试结果输出
如上图所示,可检测到三个事件的发布:ContextRefreshedEvent、EmailEvent、ContextClosedEvent。
2.7 补充说明Spring提供的内置事件
- ContextRefreshedEvent:ApplicationContext容器初始化或刷新时触发该事件(如上面测试结果)。该事件触发说明ApplicationContext容器已就绪可用。
- ContextStartedEvent:当使用ConfigurableApplicationContext接口的start()方法启动ApplicationContext容器时触发该事件。
- ContextClosedEvent:当使用ConfigurableApplicationContext接口的close()方法关闭ApplicationContext容器时触发该事件(如上面测试结果)。
- ContextStoppedEvent:当使用ConfigurableApplicationContext接口的Stop()方法使ApplicationContext停止时触发该事件。
- RequestHandledEvent:Web相关的事件,只能应用于使用DispatcherServlet的Web、应用中,在使用SpringMVC作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。
- 其他事件:Spring4.0.3后新增用于WebSocket的事件:SessionConnectedEvent、SessionConnectEvent、SessionDisconnectEvent
3. 让Bean获取Spring容器
前面的实例中,都是由程序显示的创建Spring容器,在这种访问模式下,程序中总是持有Spring容器的引用。但在Web应用中,Spring容器通常采用在web.xml中声明式方式配置产生,程序Bean处于容器管理之下,Bean无需主动访问容器。但是当需要借助Spring容器实现特定功能(如上述国际化、事件机制)时,就必须让Bean先获取Spring容器,然后借助Spring容器实现该功能。
先设定需求:Person类sayHi(String name)方法必须能输出国际化消息,由于国际化功能需要借助于Spring容器实现,因此必须让Person获取到Spring容器对象,以便其拥有国际化的能力。
3.1 Person类实现ApplicationContextAware接口,并提供相应的setter方法
为了让一个Bean获取它所在的Spring容器,可以让该Bean实现ApplicationContextAware接口,并复写接口里的唯一方法:
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
这个方法将有Spring在创建该Bean对象时调用,以便在Spring容器调用该方法时,将容器自身作为参数传入该方法。
/** * 通过继承ApplicationContextAware接口,获取Application容器,从而调用国际化案例 */ public class Person implements ApplicationContextAware { // 将BeanFactory容器以成员变量保存 private ApplicationContext ctx; /** * Spring容器会检测容器中所有Bean,如果发现某个Bean实现了 * ApplicationContextAware接口,Spring容器会在创建该Bean之后, * Spring自动调用该Bean的setApplicationContext()方法,调用该方法时, * Application会将容器本身作为参数传给该方法。 */ public void setApplicationContext(ApplicationContext ctx) throws BeansException { this.ctx = ctx; } public void sayHi(String name) { System.out.println(ctx.getMessage("hello" , new String[]{name}, Locale.getDefault(Locale.Category.FORMAT))); } }
3.2 配置Spring配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <!-- 加载容器国际化所需要的语言资源文件 --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>com/wj/chapter2/applicationContextg/i18/message</value> </list> </property> </bean> <!-- Spring容器会检测容器中所有Bean,如果发现某个Bean实现了 ApplicationContextAware接口,Spring容器会在创建该Bean之后, Spring自动调用该Bean的setApplicationContext()方法,调用该方法时, Application会将容器本身作为参数传给该方法。--> <bean id="person" class="com.wj.chapter2.applicationContextg.ApplicationContextAware.Person"/> </beans>
由配置文件可以看出,配置Person对象Bean时,与配置其他Bean并没有区别。
3.3 编写测试程序
public class Main { // 1.指明xml配置文件位置,便于Spring读取,从而知道Bean的相关信息 private static final String PATH_XML = "com/wj/chapter2/applicationContextg/ApplicationContextAware/applicationContext.xml"; @SuppressWarnings("resource") public static void main(String[] args) { // 2.根据xml配置文件,创建Spring IOC容器的上下文 ApplicationContext ctx = new ClassPathXmlApplicationContext(PATH_XML); // 3.获取Person对象,调用国际化功能 ctx.getBean("person" , Person.class).sayHi("熊燕子"); } }
3.4 查看测试结果
可以看出,的确安装资源文件的格式输出,说明Person类拿到ApplicationContext容器后具有了国际化能力。
代码下载地址:http://pan.baidu.com/s/1skOq7El,密码:qpi5
上一篇: 应用国际化(3)