spring学习(十二)——spring官方文档阅读(5.0.7)——ApplicationContext的额外能力

xiaoxiao2021-03-01  19

ApplicationContext的额外能力

Spring IOC容器也提供了下列功能:

1、负责国际化的MessageSource接口

2、通过ResourceLoader接口来加载资源

3、负责事件发布的ApplicationEventPublisher,监听器ApplicationListener

4、提供父容器访问功能的HierarchicalBeanFactory

这里只介绍前三项

MessageSource

ApplicationContext接口继承并扩展了MessageSource,提供了国际化的能力,HierarchicalMessageSource接口可以分层次的解析消息,MessageSource接口提供了下列方法:

String getMessage(String code,Object[] args,String default,Locale loc):如果在locale中没有检索到对应消息,则使用default指定的消息,args指定占位符的值,code指定消息名,loc指定国际化信息

 

String getMessage(String code,Object[] args,Locale loc):与前面的函数功能一样,不一样的是缺少了default参数,如果没有找到对应的消息,会抛出NoSuchMessageException异常

 

String getMessage(MessageSourceResolvable resolvable,Locale locale):上述两种方法所用到的参数全部封装在MessageSourceResolvable,功能与前两者一样

 

当ApplicationContext初始化后,会查找MessageSource实现类对应的bean,这个bean的名字必须是messageSource,如果这样的bena被查找到,所有之前之前描述的方法都会由这个bean调用,否则,ApplicationContext会查找父容器,如果父容器也没有查找到,一个空的DelegatingMessageSource对象会被实例化,负责上述方法的调用,MessageSource只能查看类路径下(classpath)的文件

 

Spring提供了两种MessageSource的实现——ResourceBundleMessageSource和StaticMessageSource,两者都继承并实现了HierarchicalMessageSource(可以嵌套处理message),StaticMessageSource很少使用,ResourceBundleMessageSource如下使用:

<beans> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>format</value> <value>exceptions</value> <value>windows</value> </list> </property> </bean> </beans>

在basenames属性中可以指定资源文件,上述例子假设我们有三个资源文件,名为format、exceptions、windows,如下:

# in format.properties message=Alligators rock! # in exceptions.properties argument.required=The {0} argument is required.

省略了windwos.properties文件,由于ApplicationContext本身继承了MessageSource接口,所以可以如下使用:

public static void main(String[] args) { MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); String message = resources.getMessage("message", null, "Default", null); System.out.println(message); }

输出结果为:Alligators rock!

上述例子中exceptions.properties使用了占位符,可以在getMessage函数中指明占位符的值:

public class Example { private MessageSource messages; public void setMessages(MessageSource messages) { this.messages = messages; } public void execute() { String message = this.messages.getMessage("argument.required", new Object [] {"userDao"}, "Required", null); System.out.println(message); } }

运行结果为:The userDao argument is required.

之前介绍的功能都没有涉及到国际化,接下来介绍一下国际化的方式:

假设我们想根据英国(en-GB)语言环境解析消息,我们需要创建三个文件(format_en_GB.properties, exceptions_en_GB.properties, 、 windows_en_GB.properties),其中一个文件的内容为:

# in exceptions_en_GB.properties argument.required=Ebagum lad, the {0} argument is required, I say, required.

获得其中的内容:

public static void main(final String[] args) { MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); String message = resources.getMessage("argument.required", new Object [] {"userDao"}, "Required", Locale.UK); System.out.println(message); }

运行结果:Ebagum lad, the 'userDao' argument is required, I say, required.

 

标准和自定义事件

ApplicationListener接口会处理事件,每次ApplicationEventPublisher实现类将事件发布到ApplicationContext中时,都会通知ApplicationListener,两者使用的是观察者模式

Spring提供了下列标准事件:

ContextRefreshedEvent:当Application初始化或是刷新(注册了一个Bean以后会使用refresh()刷新容器)完毕,容器初始化完毕意味着所有的bean都被初始化,容器可以被正常使用。

ContextStartedEvent:当ApplicationContext启动时发布,启动是指调用ConfigurableApplication接口的start()方法,此时所有继承并实现了Lifecycle的的bean将会接收到启动信息

ContextStoppedEvent:当ApplicationContext停止时发布,停止是指调用ConfigurableApplication接口的stop()方法,此时所有继承并实现了Lifecycle的的bean将会接收到停止信息

ContextClosedEvent:当ApplicationContext关闭时发布,关闭是指调用ConfigurableApplication接口的close()方法,此时所有的单例bean都会被摧毁,此时容器完全被摧毁,无法刷新或是重新启动

RequestHandledEvent:HTTP请求处理完毕后发布的事件,只在web应用中有效(即使用DispatcherServlet)

spring允许我们自定义事件处理机制,首先先要定义事件类,通过继承ApplicationEvent类,即可成为事件类:

public class BlackListEvent extends ApplicationEvent { private final String address; private final String test; public BlackListEvent(Object source, String address, String test) { super(source); this.address = address; this.test = test; } // accessor and other methods... }

事件的发布需要通过ApplicationEventPublisher接口,该接口的publishEvent()方法即可发布事件,事件得发布一般通过继承ApplicationEventPublisherAware接口实现

public class EmailService implements ApplicationEventPublisherAware { private List<String> blackList; private ApplicationEventPublisher publisher; public void setBlackList(List<String> blackList) { this.blackList = blackList; } public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } public void sendEmail(String address, String text) { if (blackList.contains(address)) { BlackListEvent event = new BlackListEvent(this, address, text); publisher.publishEvent(event); return; } // send email... } }

在配置阶段,Spring容器会检测到EmailService继承了ApplicationEventPublisherAware接口,会自动调用该接口的setApplicationEventPublisher()方法,该方法传入的是Spring容器本身(其继承实现了ApplicationEventPublisher),此时便可以将事件发布到容器当中,为了接收到事件,需要继承ApplicationListerner接口:

public class BlackListNotifier implements ApplicationListener<BlackListEvent> { private String notificationAddress; public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificationAddress; } public void onApplicationEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress... } }

注意到ApplicationListener<BlackListEvent>,意味着这个类为BlackListEvent的事件监听器,由它负责处理BlackListEvent事件,监听器同步接收事件并进行处理,意味着publishEvent()方法是阻塞的,接着onApplicationEvent方法会被调用处理事件

 

基于注解的事件监听器

事件监听器除了继承ApplicationListener实现外,也可以使用@EventListener注解,可以将上述例子的BlackListNotifier更改为:

public class BlackListNotifier { private String notificationAddress; public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificationAddress; } @EventListener public void processBlackListEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress... } }

使用注解,不用继承接口,同时事件监听的方法名称也可以自定义,监听的事件也可以通过函数参数类型进行解析,如果一个方法需要监听许多事件或者不想使用函数参数,也可以如下定义:

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class}) public void handleContextStart() { ... }

通过注解的condition属性,也可以监听满足一定条件的事件,condition属性的值是SPEL表达式:

@EventListener(condition = "#blEvent.test == 'foo'") public void processBlackListEvent(BlackListEvent blEvent) { // notify appropriate parties via notificationAddress... }

上述例子表明监听事件必须含有test属性,并且该属性值为foo,当我们处理完这个事件后,也可以发布另外一个事件(通过函数返回值):

@EventListener public ListUpdateEvent handleBlackListEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress and // then publish a ListUpdateEvent... }

如果想发布多个事件,则返回事件的Collection,异步监听器不支持这种使用方式

 

异步监听器

@Async注解允许异步执行方法,用于监听器,可实现异步监听器:

@EventListener @Async public void processBlackListEvent(BlackListEvent event) { // BlackListEvent is processed in a separate thread }

是用异步监听器需要注意以下几点:

1、如果监听器抛出异常,会将异常传递给调用者,更多详情请查看AsyncUncaughtExceptionHandler

2、不允许通过return的方式发布事件

 

监听器的执行顺序

一个事件可以有多个监听器处理,此时我们可以通过@Order注解声明执行顺序,值越大越慢执行

@EventListener @Order(42) public void processBlackListEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress... }

 

在监听器中使用泛型

监听器的参数允许使用泛型

@EventListener public void onPersonCreated(EntityCreatedEvent<Person> event) { ... }

 

加载资源文件

Applicationcontext是一个资源加载器,资源的位置通过url的方式指定,只要能通过url定位的资源都可以导入

我们也可以使用Resource类型来获得静态资源,它可以作为依赖被注入,当将一个 ResourceLoaderAware 接口的实现类部署到应用上下文时(此类会作为一个 spring 管理的 bean), 应用上下文会识别出此为一个 ResourceLoaderAware 对象,并将自身作为一个参数来调用 setResourceLoader() 函数,如此,该实现类便可使用 ResourceLoader 获取 Resource 实例来加载你所需要的资源,更多详情请看:https://blog.csdn.net/xiangjai/article/details/53954252

转载请注明原文地址: https://www.6miu.com/read-3650027.html

最新回复(0)