Spring之ApplicationContext的拓展功能
相对于BeanFactory来说,ApplicationContext除了提供BeanFactory的所有功能外,还有一些其他的功能,主要包括国际化支持、资源访问、事件传递。下面将讲解ApplicationContext在BeanFactory的基本功能之外的功能。
1.国际化支持
国际化的英文为Internationalization,这个也太长了,所以它又称为I18n(英文单词 internationalization的首末字符i和n,18为中间的字符数)。 国际化的操作就是指一个程序可以同时适应多门语言,即:如果现在程序的使用者是中国人,则会以中文为显示文字,如果现在程序的使用者是美国人,则会以英语为显示的文字,也就是说可以通过国际化操作,让一个程序适应各个国家的语言要求。
程序根据不同的语言环境找到不同的资源文件,之后从资源文件中取出内容,资源文件中的内容是以key-àvalue的形式保存的,所以在读取的时候通过其key找到对应的value
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>message</value>
</list>
</property>
</bean>
</beans>
k1=welcome\uFF1A {0}
3、message_zh_CN.properties
k1=\u6b22\u8fce\u4f60\uff0c{0}
k1就是“你好”,上面使用的是unicode。可以使用Unicode的转换器来进行转换,也可以使用命令
4、测试
package com.yj.spring;
import java.util.Locale;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class I18NTest {
@Test
public void Test() {
Locale defaultLocale = Locale.getDefault();
//Locale defaultLocale = Locale.US;
System.out.println("country="+ defaultLocale.getCountry());
System.out.println("language="+ defaultLocale.getLanguage());
Object[] arg = new Object[] { "悟空"};
ApplicationContext ctx = new ClassPathXmlApplicationContext("i18n.xml");
String msg = ctx.getMessage("k1", arg, defaultLocale);
System.out.println(msg);
}
}
5、默认时,显示为
country=CN
language=zh
欢迎你,悟空
当为美国US时,显示为
country=US
language=en
welcome: 悟空
2.资源访问
很多时候应用程序都需要存取资源。Spring提供了对资源文件的存取。ApplicationContext继承了ReourceLoader接口,开发人员可以使用getResource()方法并指定资源文件的URL来存取。
ApplicationContext对资源文件的读取有如下3种方式:
1.虚拟路径来存取;
如果资源文件位于CLASSPATH下:可以通过这种方式来获取,代码如下
Resource resource=ctx.getResource("classpath:message.properties");
这里要说明的是"claspath:"是 spring约定的URL虚拟路径。
2.绝对路径来存取;
指定标准的URL,例如“file:”或“http:”,代码如下
Resource source=ctx.getResource("file:D:/eclipse/workspace/Spring/src/main/resources/message.properties");
3.相对路径来存取。
Resource source = actx.getResource("WEB-INF/message.properties");
当通过ApplicationContext取得一个Resource后,开发人员可以使用:
- getFile() 来存取资源文件内容
- exists()来检查资源文件是否存在
- isOpen()检查资源文件是否被打开
- getURL()返回资源文件的URL
完整test文件:
package com.yj.spring;
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.Resource;
public class PropTest {
@Test
public void ClassPathTest() throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("Beans.xml");
Resource resource=ctx.getResource("classpath:message.properties");
File file=resource.getFile();
Properties prop=fileToProp(file);
System.out.println("通过classptah路径获取:"+prop.getProperty("name"));
}
@Test
public void RealPathTest() throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("Beans.xml");
Resource resource=ctx.getResource("file:D:/eclipse/workspace/Spring/src/main/resources/message.properties");
File file=resource.getFile();
Properties prop=fileToProp(file);
System.out.println("通过实际路径获取:"+prop.getProperty("name"));
}
@Test
public void virtualPathTest() throws Exception {
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:/eclipse/workspace/Spring/src/main/resources/Beans.xml");
Resource resource=ctx.getResource("WEB-INF/message.properties");
File file=resource.getFile();
Properties prop=fileToProp(file);
System.out.println("通过虚拟路径获取:"+prop.getProperty("name"));
}
private Properties fileToProp(File file) throws Exception{
Properties prop = new Properties();
prop.load(new FileInputStream(file));
return prop;
}
}
3.事件传递
ApplicationContext事件机制是观察者设计模式(订阅/发布模式)的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。
Spring的事件框架有如下两个重要的成员:
-
ApplicationEvent:容器事件,必须由ApplicationContext发布
-
ApplicationListener:监听器,可由容器中的任何监听器Bean担任
实际上,Spring的事件机制与所有时间机制都基本相似,它们都需要事件源、事件和事件监听器组成。只是此处的事件源是ApplicationContext,且事件必须由Java程序显式触发。下面的程序将演示Spring容器的事件机制。
Demo
1.项目整体结构
2.EmailController
package com.yj.event.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.yj.event.email.EmailEvent;
import com.yj.event.email.EmailService;
@RestController
public class EmailController {
@Autowired
private EmailService emailService;
@RequestMapping("/sendEmail")
public void sendEmail() {
EmailEvent emailEvent = new EmailEvent("source");
emailEvent.setAddress("my address");
emailEvent.setText("hello world");
emailService.sendEmail(emailEvent);
}
}
3.EmailEvent
程序先定义了一个EmailEvent类,其对象就是一个Spring容器事件。该类继承了ApplicationEvent类,除此之外,它就是一个普通的Java类。代码如下:
package com.yj.event.email;
import org.springframework.context.ApplicationEvent;
public class EmailEvent extends ApplicationEvent {
private static final long serialVersionUID = 8890656093518139995L;
private String address;
private String text;
public EmailEvent(Object source) {
super(source);
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
4.EmailListener
两种实现方式,
- @EventListener注解
- 一种实现ApplicationListener接口,重写onApplicationEvent方法
基于注解的方式
package com.yj.event.email;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class EmailAnnoListener {
@EventListener
//@Async
public void EmailEventListener(EmailEvent event) {
try {
System.out.println("开始休眠...");
Thread.sleep(5000L);
System.out.println("休眠结束...");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("EmailEventListener:"+event.getClass());
System.out.println("注解监听到发送邮件的事件");
System.out.println("注解需要发送的邮件地址: " + event.getAddress());
System.out.println("注解邮件正文: " + event.getText());
}
}
实现接口的方式
package com.yj.event.email;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
//@Component
//@Async
public class EmailListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("EmailListener:" + event.getClass());
if (event instanceof EmailEvent) {
EmailEvent emailEvent = (EmailEvent) event;
System.out.println("监听到发送邮件的事件");
System.out.println("需要发送的邮件地址: " + emailEvent.getAddress());
System.out.println("邮件正文: " + emailEvent.getText());
try {
Thread.sleep(5000L);
System.out.println("休眠...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
EmailEventListener方法或者onApplicationEvent方法,可以传入ApplicationEvent参数,监听所有的事件,本例只传入EmailEvent参数,只监听EmailEvent事件。然后将监听器配置在Spring的容器中。需注意的是,此时事件的发布,与事件的监听处理,默认是同步阻塞的,下文会开启异步的方式
5.EmailService
当系统创建Spring容器、加载Spring容器时会自动触发容器事件,容器事件监听器可以监听到这些事件。除此之外,程序也可以调用ApplicationContext的publishEvent()方法来主动触发一个容器事件
package com.yj.event.email;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import com.yj.event.util.ApplicationContextUtil;
@Component
public class EmailService {
public void sendEmail(EmailEvent event){
ApplicationContext ctx=ApplicationContextUtil.getApplicationContext();
ctx.publishEvent(event);
}
}
6.ApplicationContextUtil
如果Bean想发布事件,则Bean必须获得其容器的引用。如果程序中没有直接获取容器的引用,则应该让Bean实现ApplicationContextAware或者BeanFactoryAware接口,从而可以获得容器的引用
package com.yj.event.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (ApplicationContextUtil.applicationContext == null) {
ApplicationContextUtil.applicationContext = applicationContext;
}
}
}
7.pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yj</groupId>
<artifactId>Event</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Event</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath />
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
8.app
package com.yj.event;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
//@EnableAsync
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
验证
访问路径
http://127.0.0.1:8080/sendEmail
默认同步的情况下,显示结果
开始休眠...
休眠结束...
EmailEventListener:class com.yj.event.email.EmailEvent
注解监听到发送邮件的事件
注解需要发送的邮件地址: my address
注解邮件正文: hello world
结束
开启异步的情况(app.java类添加@EnableAsync注解,EmailAnnoListener类的EmailEventListener方法上添加@Async注解),有点MQ的效果,只是只能在一个ApplicationContext范围内才能捕获到事件的发布,才能起作用。显示结果
结束
开始休眠...
休眠结束...
EmailEventListener:class com.yj.event.email.EmailEvent
注解监听到发送邮件的事件
注解需要发送的邮件地址: my address
注解邮件正文: hello world
Spring提供如下几个内置事件:
-
ContextRefreshedEvent:ApplicationContext容器初始化或刷新时触发该事件。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并**,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用
-
ContextStartedEvent:当使用ConfigurableApplicationContext(ApplicationContext的子接口)接口的start()方法启动ApplicationContext容器时触发该事件。容器管理声明周期的Bean实例将获得一个指定的启动信号,这在经常需要停止后重新启动的场合比较常见
-
ContextClosedEvent:当使用ConfigurableApplicationContext接口的close()方法关闭ApplicationContext时触发该事件
-
ContextStoppedEvent:当使用ConfigurableApplicationContext接口的stop()方法使ApplicationContext容器停止时触发该事件。此处的停止,意味着容器管理生命周期的Bean实例将获得一个指定的停止信号,被停止的Spring容器可再次调用start()方法重新启动
-
RequestHandledEvent:Web相关事件,只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。
上一篇: htmlspecialchars($value)导致中文丢失解决方案
下一篇: disucz代码分析
推荐阅读
-
PHP V5.2新增功能之第1部分:使用新的内存管理器_PHP教程
-
EasyNVR摄像机网页无插件直播方案H5前端构建之:bootstrap弹窗功能的实现方案与代码
-
在ecshop的购物流程页,商品列表加一个复选框,并实现与之相对应功能的代码,该怎么处理
-
基于maven的旅游网站设计之邮箱验证功能实现
-
ES6之增强的数组功能
-
java JSP开发之Spring中Bean的使用
-
java开发之spring webflow实现上传单个文件及多个文件功能实例
-
使用spring框架中的组件发送邮件功能说明
-
Java开发之spring security实现基于MongoDB的认证功能
-
Spring Boot基础入门之基于注解的Mybatis