Spring容器启动过程中发布的核心事件及事件处理机制详解

开发 项目管理
到Spring 4.2为止,事件基础设施得到了显著改进,提供了基于注释的模型以及发布任意事件的能力(也就是说,不一定是从ApplicationEvent扩展的对象)。当这样的对象被发布时,我们将它包装在一个事件中。

核心事件

ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口提供的。如果将实现一个Bean实现了ApplicationListener接口,那么每当ApplicationEvent发布到ApplicationContext时,就会通知该bean。本质上,这是标准的观察者设计模式。

到Spring 4.2为止,事件基础设施得到了显著改进,提供了基于注释的模型以及发布任意事件的能力(也就是说,不一定是从ApplicationEvent扩展的对象)。当这样的对象被发布时,我们将它包装在一个事件中。

下表列出了Spring提供的标准事件:

Event

Explanation

ContextRefreshedEvent

在ApplicationContext被初始化或刷新时发布(例如,通过使用ConfigurableApplicationContext接口上的refresh()方法)。这里的“初始化”意味着加载了所有bean,检测并激活了后处理器bean,预实例化了单例,并且ApplicationContext对象已经准备好可以使用。只要上下文没有关闭,就可以多次触发刷新,前提是所选择的ApplicationContext实际上支持这种“热”刷新。

ContextStartedEvent

通过使用ConfigurableApplicationContext接口上的start()方法启动ApplicationContext时发布。在这里,“started”意味着所有生命周期bean都接收一个显式的开始信号。通常,该信号用于在显式停止之后重新启动bean,但也可以用于启动未配置为自动启动的组件(例如,在初始化时尚未启动的组件)。

ContextStoppedEvent

当通过使用ConfigurableApplicationContext接口上的stop()方法停止ApplicationContext时发布。在这里,“stopped”意味着所有生命周期bean都接收一个明确的停止信号。停止的上下文可以通过start()调用重新启动。

ContextClosedEvent

使用ConfigurableApplicationContext接口上的close()方法或通过JVM关闭挂钩关闭ApplicationContext时发布。在这里,“closed”意味着所有的单例bean都会被销毁。一旦上下文被关闭,它就会到达生命的终点,并且不能被刷新或重启。

RequestHandledEvent

一个特定于web的事件,告诉所有bean一个HTTP请求已经得到了服务。此事件在请求完成后发布。这个事件只适用于使用Spring的DispatcherServlet的web应用程序。

ServletRequestHandledEvent

RequestHandledEvent的一个子类,用于添加特定于servlet的上下文信息。

以上事件发布时机:

  • ContextRefreshedEvent

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void refresh() {
// ...
finishRefresh();
}
}

  • ContextStartedEvent

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void refresh() {
// ...
finishRefresh();
}
protected void finishRefresh() {
// 初始化LifecycleProcessor(DefaultLifecycleProcessor)
initLifecycleProcessor();
getLifecycleProcessor().onRefresh();
}
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
}
public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware {
public void start() {
startBeans(false);
this.running = true;
}
}

  • ContextStoppedEvent

该事件与上面的started是对应的

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void stop() {
getLifecycleProcessor().stop();
publishEvent(new ContextStoppedEvent(this));
}
}
public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware {
public void stop() {
stopBeans();
this.running = false;
}
}

  • ContextClosedEvent

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void close() {
synchronized (this.startupShutdownMonitor) {
doClose();
}
}
protected void doClose() {
publishEvent(new ContextClosedEvent(this));
}
}

  • ServletRequestHandledEvent

public abstract class FrameworkServlet {
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) {
publishRequestHandledEvent(request, response, startTime, failureCause);
}
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response, long startTime, @Nullable Throwable failureCause) {
if (this.publishEvents && this.webApplicationContext != null) {
// Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() - startTime;
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, response.getStatus()));
}
}
}

你还可以创建和发布自己的自定义事件。下面的例子展示了一个简单的类,它扩展了Spring的ApplicationEvent基类:

public class BlockedListEvent extends ApplicationEvent {


private final String address;
private final String content;


public BlockedListEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}
}

要发布自定义的ApplicationEvent,需要调用ApplicationEventPublisher的publishEvent()方法。通常,这是通过创建一个实现ApplicationEventPublisherAware的类并将其注册为Spring bean来完成的。下面的例子展示了这样一个类:

public class EmailService implements ApplicationEventPublisherAware {


private List<String> blockedList;
private ApplicationEventPublisher publisher;


public void setBlockedList(List<String> blockedList) {
this.blockedList = blockedList;
}


public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}


public void sendEmail(String address, String content) {
if (blockedList.contains(address)) {
publisher.publishEvent(new BlockedListEvent(this, address, content));
return;
}
}
}

在配置时,Spring容器检测到EmailService实现了ApplicationEventPublisherAware,并自动调用
setApplicationEventPublisher()。实际上,传入的参数是Spring容器本身。通过ApplicationEventPublisher接口与应用程序上下文进行交互。

要接收自定义的ApplicationEvent,可以创建一个实现ApplicationListener的类,并将其注册为Spring bean。下面的例子展示了这样一个类:

public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {


private String notificationAddress;


public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}


public void onApplicationEvent(BlockedListEvent event) {
}
}

请注意,ApplicationListener通常参数化为自定义事件的类型。这意味着onApplicationEvent()方法可以保持类型安全,避免任何向下转换的需要。你可以注册任意数量的事件监听器,但请注意,默认情况下,事件监听器是同步接收事件的。这意味着publishEvent()方法会阻塞,直到所有监听器都完成事件处理。这种同步和单线程方法的一个优点是,当侦听器接收到事件时,如果事务上下文可用,它将在发布者的事务上下文内操作。

通过注解监听事件

可以使用@EventListener注解在托管bean的任何方法上注册事件监听器。可以将BlockedListNotifier重写为:

public class BlockedListNotifier {


private String notificationAddress;


public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}


@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
}
}

同时监听多个事件

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

异步事件

如果希望特定的监听器异步处理事件,可以重用常规的@Async支持。如下面的例子所示:

@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
}

在使用异步事件时,请注意以下限制:

  1. 如果异步事件监听器抛出异常,则异常不会传播到调用者。
  2. 异步事件监听器方法无法通过返回值发布后续事件。如果你需要发布另一个事件作为处理的结果,注入一个ApplicationEventPublisher来手动发布事件。

事件监听顺序

如果需要在调用另一个监听器之前调用一个监听器,可以在方法声明中添加@Order注解,如下面的例子所示:

@EventListener
@Order(1)
public void processBlockedListEvent(BlockedListEvent event) {
}

通用的事件

你还可以使用泛型来进一步定义事件的结构。考虑使用EntityCreatedEvent<T>,其中T是实际创建的实体的类型。例如,你可以创建以下监听器定义,只接收Person的EntityCreatedEvent:

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

由于类型擦除,只有当触发的事件解析了事件监听器过滤的泛型参数(即类似于PersonCreatedEvent类扩展EntityCreatedEvent<Person>{…})时,才会起作用。

事件触发原理

方式1:ApplicationEventPublisher

AbstractApplicationContext实现了ApplicationEventPublisher接口,那么只要ApplicationContext继承自AbstractApplicationContext都可以直接发布事件:

public abstract class AbstractApplicationContext {
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
} else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}
}

方式2:通过@EventListener注解

该注解是由EventListenerMethodProcessor处理器处理的。

public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 获取容器中注册的事件监听工厂,可以有多个
Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
}
public void afterSingletonsInstantiated() {
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
processBean(beanName, type);
}
}
private void processBean(final String beanName, final Class<?> targetType) {
Map<Method, EventListener> annotatedMethods = null;
try {
// 取得Bean内带有@EventListener注解的方法
annotatedMethods = MethodIntrospector.selectMethods(targetType, (MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
// 遍历找到的所有@EventListener注解的方法
for (Method method : annotatedMethods.keySet()) {
// 遍历所有的EventListenerFactory
for (EventListenerFactory factory : factories) {
// 判断当前的事件监听工厂是否支持当前的方法
if (factory.supportsMethod(method)) {
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
// 创建对应的事件监听程序
ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
context.addApplicationListener(applicationListener);
break;
}
}
}
}
}


责任编辑:武晓燕 来源: 实战案例锦集
相关推荐

2009-09-02 18:34:28

C#鼠标事件

2011-07-01 14:20:59

Qt 事件

2011-07-01 14:14:34

Qt 事件

2023-02-23 08:15:33

Spring异常处理机制

2011-03-17 09:20:05

异常处理机制

2023-06-15 14:09:00

解析器Servlet容器

2011-09-05 17:35:18

MTK启动过程RTOS

2013-08-07 14:48:00

HTML5

2023-09-14 15:15:36

2011-06-28 13:27:13

ARM Linux

2010-09-16 09:37:21

JavaScript事

2023-09-07 10:31:27

2015-11-06 13:59:01

JavaScript事件处理

2010-03-05 15:40:16

Python异常

2017-01-11 18:44:43

React Nativ触摸事件Android

2021-09-28 15:03:06

Linux内核arm

2011-07-04 14:38:43

QT Qevent

2009-08-04 13:53:58

C#委托类C#事件

2019-05-27 14:43:49

Tomcat架构部署

2020-04-20 21:30:51

Tomcat部署架构
点赞
收藏

51CTO技术栈公众号