Spring
框架自带了事件监听机制,可以轻松实现观察者模式。在Spring / Spring Boot
框架中内置了许多事件,同时开发者也可基于这些自定义发布应用程序事件。其中主要涉及以下几个核心类与接口。
1.ApplicationEvent
应用程序事件
ApplicationEvent
应用程序事件是一个抽象类,相当于观察者模式中的观察目标。ApplicationEvent
源码如下:
package org.springframework.context;
import java.time.Clock;
import java.util.EventObject;
public abstract class ApplicationEvent extends EventObject {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 7099057708183571937L;
/** System time when the event happened. */
private final long timestamp;
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public ApplicationEvent(Object source, Clock clock) {
super(source);
this.timestamp = clock.millis();
}
public final long getTimestamp() {
return this.timestamp;
}
}
ApplicationEvent
继承自java.util.EventObject
事件对象类,Spring
框架中所有事件都继承自ApplicationEvent
抽象类,是所有事件的父类。
ApplicationEvent
主要的核心是构造函数,它可以初始化一个source
事件关联对象,以便在事件监听器中获取并通知更新。
2.ApplicationListener
应用程序事件监听器
ApplicationListener
应用程序事件监听器是一个接口,相当于观察者模式中的观察者。ApplicationListener
源码如下:
package org.springframework.context;
import java.util.EventListener;
import java.util.function.Consumer;
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
return (event) -> {
consumer.accept(event.getPayload());
};
}
}
ApplicationListener
继承自java.util.EventListener
事件监听接口,ApplicationListener
接口中只定义了一个void onApplicationEvent(E event)
方法。当指定监听的事件被发布后就会被触发执行,可以通过event
获取事件中的关联对象。
3.ApplicationEventPublisher
应用事件发布者
ApplicationEventPublisher
应用程序事件发布接口,封装了事件发布的基础功能。ApplicationEventPublisher
接口源码如下:
package org.springframework.context;
@FunctionalInterface
public interface ApplicationEventPublisher {
/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an application event. Events may be framework events
* (such as ContextRefreshedEvent) or application-specific events.
* <p>Such an event publication step is effectively a hand-off to the
* multicaster and does not imply synchronous/asynchronous execution
* or even immediate execution at all. Event listeners are encouraged
* to be as efficient as possible, individually using asynchronous
* execution for longer-running and potentially blocking operations.
* @param event the event to publish
* @see #publishEvent(Object)
* @see org.springframework.context.event.ContextRefreshedEvent
* @see org.springframework.context.event.ContextClosedEvent
*/
default void publishEvent(ApplicationEvent event) {
this.publishEvent((Object)event);
}
/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an event.
* <p>If the specified {@code event} is not an {@link ApplicationEvent},
* it is wrapped in a {@link PayloadApplicationEvent}.
* <p>Such an event publication step is effectively a hand-off to the
* multicaster and does not imply synchronous/asynchronous execution
* or even immediate execution at all. Event listeners are encouraged
* to be as efficient as possible, individually using asynchronous
* execution for longer-running and potentially blocking operations.
* @param event the event to publish
* @since 4.2
* @see #publishEvent(ApplicationEvent)
* @see PayloadApplicationEvent
*/
void publishEvent(Object event);
}
ApplicationEventPublisher
中定义了一个默认的接口方法和和普通接口方法,普通接口方法需要由具体子类容器实现。
4.ApplicationContext
应用程序上下文
ApplicationContext
是Spring
中的核心容器之一。ApplicationContext
接口继承了ApplicationEventPublisher
接口,所以可以使用ApplicationContext
接口来发布事件。
以上所说的Spring
事件监听发布,通过ApplicationEventPublisher
或者ApplicationContext
容器发布事件,使用ApplicationEvent
关联事件对象。然后ApplicationListener
监听该事件。当事件发布后,监听器就会监听到事件的发布,并获取到事件关联对象。
5.Spring
观察者模式实战
首先创建一个Spring Boot
工程。然后创建事件实体:
public class EventBO {
private String name;
public EventBO(){}
public EventBO(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "EventBO{" +
"name='" + name + '\'' +
'}';
}
}
创建应用事件:
public class TestApplicationEvent extends ApplicationEvent {
public TestApplicationEvent(EventBO eventBO) {
super(eventBO);
}
}
创建事件监听器:
@Component
public class TestApplicationListener implements ApplicationListener<TestApplicationEvent> {
@Override
public void onApplicationEvent(TestApplicationEvent event) {
EventBO source = (EventBO) event.getSource();
System.out.println("Hello " + source.getName() + " this is Spring Application Event!");
}
}
创建应用上下文:
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (ApplicationContextHolder.applicationContext == null) {
ApplicationContextHolder.applicationContext = applicationContext;
}
}
public static <T> void publishEvent(T applicationEvent) {
applicationContext.publishEvent(applicationEvent);
}
}
在Controller
中声明一个接口用于手动触发事件:
@RestController
public class HelloWorldController {
@GetMapping("event/{name}")
public void event(@PathVariable("name") String name) {
ApplicationContextHolder.publishEvent(new TestApplicationEvent(new EventBO(name)));
}
}
启动工程后请求接口http://127.0.0.1:9096/event/kapcb
,控制台输出如下:
Hello kapcb this is Spring Application Event!
利用Spring
中的事件监听机制可以轻松实现观察者模式,观察目标也不需要维护观察者列表。相当于发布 - 订阅模式。两者之间是完全解耦的,但是每个观察者都需要在Spring
中创建一个Bean
。