Spring之ApplicationContext的拓展功能

版权声明: https://blog.csdn.net/qq_24313635/article/details/82112565

      相对于BeanFactory来说,ApplicationContext除了提供BeanFactory的所有功能外,还有一些其他的功能,主要包括国际化支持、资源访问、事件传递。下面将讲解ApplicationContext在BeanFactory的基本功能之外的功能。

1.国际化支持

简介

     国际化的英文为Internationalization,这个也太长了,所以它又称为I18n(英文单词 internationalization的首末字符i和n,18为中间的字符数)。 国际化的操作就是指一个程序可以同时适应多门语言,即:如果现在程序的使用者是中国人,则会以中文为显示文字,如果现在程序的使用者是美国人,则会以英语为显示的文字,也就是说可以通过国际化操作,让一个程序适应各个国家的语言要求。

原理

        程序根据不同的语言环境找到不同的资源文件,之后从资源文件中取出内容,资源文件中的内容是以key-àvalue的形式保存的,所以在读取的时候通过其key找到对应的value

Demo

1、i18n.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" 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>

2、message_en_US.properties

k1=welcome\uFF1A {0}

3、message_zh_CN.properties

k1=\u6b22\u8fce\u4f60\uff0c{0}

      k1就是“你好”,上面使用的是unicode。可以使用Unicode的转换器来进行转换,也可以使用命令

4、测试

扫描二维码关注公众号,回复: 3359560 查看本文章
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处理用户请求结束后,系统会自动触发该事件。

猜你喜欢

转载自blog.csdn.net/qq_24313635/article/details/82112565
今日推荐