目录
3.事件广播器(ApplicationEventMulticaster)
5.事件发布者(ApplicationEventPublisher)
一、观察者模式
观察者模式也被称为发布-订阅模式,它是常用的行为型设计模式。在GoF的设计模式一书中它是这样定义的:在对象之间定义一个一对多依赖,当一个对象状态改变的时候,所有依赖的对象都会自动接收到通知。假如我们在开发一个系统,有一个需求是用户注册完毕之后马上发给用户一个注册成功的邮件通知。基于这种需求,简略代码可能是这样的:
public class UserController {
private UserService userService;
private EmailProcessor emailProcessor;
public void register(User user) {
userService.register(user);
emailProcessor.sendEmail(user.getMailbox());
}
}
这种情况下如果没有扩展和修改的需求勉强还可以,但是假如现在我们需要在发送完email之后,还要为新用户发放体验金,那么我们就要在register中添加发放体验金的代码,如果发放体验金的操作在某个组件中完成,我们就要将这个组件耦合进来。这种情况可以使用观察者模式解决:
//被观察者接口
public interface ISpeaker<E> {
public void addListener(IListener<E> listener); //注册观察者
public void removeListener(IListener<E> listener);
public void speakOut(E message); //发布消息
}
//观察者接口
public interface IListener<E> {
public void processMessage(E message);
}
UserController
public class UserController implements ISpeaker<User> {
private UserService userService;
private List<IListener<User>> listenerList = new ArrayList<>();
public UserController() {
userService = new UserService();
}
public void register(User user) {
userService.register(user);
//这里添加一些观察者.....
speakOut(user); // 为所有观察者发布“注册成功的消息”
}
@Override
public void addListener(IListener<User> listener) {
if (listenerList.contains(listener)) {
return;
}
listenerList.add(listener);
}
@Override
public void removeListener(IListener<User> listener) {
listenerList.remove(listener);
}
@Override
public void speakOut(User message) {
for (IListener<User> listener : listenerList) {
listener.processMessage(message);
}
}
}
此时如果注册成功那么所有的观察者就可以收到通知,并执行相关的动作,比如对于EmailProcessor就是一个观察者:
public class EmailProcessor implements IListener<User> {
public void sendEmail(String userId) {
System.out.println("为" + userId + "用户发送注册成功邮件");
}
@Override
public void processMessage(User message) {
this.sendEmail(message.getMailbox());
}
}
二、Spring事件机制中的几个概念
- 事件对象(ApplicationEvent):表示事件本身,每个接口的实现类表示一个事件类,可以传递数据。
- 事件监听器(ApplicationListener):用于捕获事件进行通知发送等后续操作,事件的业务逻辑在监听器里面处理。
- 事件广播器(ApplicationEventMulticaster):用于事件监听器的注册和事件的广播。
- 事件发布者(ApplicationEventPublisher):用于发布事件。
1.事件对象(ApplicationEvent)
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
//事件对象创建的时间戳
private final long timestamp;
//这里的参数source表示的是事件源,也就是发布事件的对象
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public final long getTimestamp() {
return this.timestamp;
}
}
ApplicationEvent继承自java.util.EventObject,这个类中维护了一个Object类型的source属性,用于记录事件源(也就是发布事件的对象)。继承这个接口只是遵照了事件模式的规范,比如Swing中的事件机制也遵照这个规范。 在我上面实现的观察者模式中,存在ISpeaker(相当于事件广播器---发布事件)和IListener(相当于事件监听器),而没有事件对象,上述的使用示例中只是针对性地处理了用户注册成功的事件,如果存在多个事件,比如还有用户注销账户地事件那么就不能进行区分,Spring中提供了事件本身ApplicationEvent,方便处理多种事件类型。
Spring中内置的事件对象:
- ContextStartedEvent:该事件在AbstractApplicationContext的refresh()方法中的最后一步finishRefresh()中被触发。可以认为当我们的ApplicationContext构造完毕,或者每次执行refresh()方法时才能触发。
- ContextStoppedEvent:ApplicationContext停止后(容器的stop()方法)触发的事件。
- ContextStartedEvent:ApplicationContext启动后(容器的start()方法)触发的事件。
- ContextClosedEvent:ApplicationContext关闭(销毁--销毁方法)后触发的事件。
- RequestHandledEvent:请求完成后触发。这个事件只适用于使用Spring的DispatcherServlet的web应用程序。
2.事件监听器(ApplicationListener)
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
Java的事件监听者需要实现EventListener接口,这并不是必须,只是一种规范。EventListener是一个标记性接口,内部并没有提供任何方法。自定义的事件监听器需要实现这个onApplicationEvent(E event)方法,通过该方法我们捕获到事件对象。然后在该方法中进行后续的操作。Spring中还有一个SmartApplicationListener接口,它继承了ApplicationListener和Orderd两个接口,具有排序功能。
3.事件广播器(ApplicationEventMulticaster)
public interface ApplicationEventMulticaster {
void addApplicationListener(ApplicationListener<?> listener);
void addApplicationListenerBean(String listenerBeanName);
void removeApplicationListener(ApplicationListener<?> listener);
void removeApplicationListenerBean(String listenerBeanName);
void removeAllListeners();
void multicastEvent(ApplicationEvent event);
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}
这个跟上面观察者模式的ISpeaker比较像,用于注册事件的监听者和广播事件。前面几个注册/移除监听者的方法在抽象类AbstractApplicationEventMulticaster中实现,他将监听者保存在一个Set中(无序,如果需要我们可以使用有序的监听器SmartApplicationListener,它实现了Orederd接口),如下是AbstractApplicationEventMulticaster中的一个添加监听器的方法实现:
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.retrievalMutex) {
//如果已经注册,则显式删除代理的目标,以避免重复调用同一侦听器。
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
//Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
Spring中SimpleApplicationEventMulticaster实现了后两个广播事件的方法,如下:
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//这里获取的是当前对象的线程池成员,该类中维护了一个private Executor taskExecutor;
Executor executor = getTaskExecutor();
//根据事件类型获取对应的监听器集合
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
//如果线程池对象不为null,则可以异步响应事件
executor.execute(() -> invokeListener(listener, event));
}else {
//同步方式执行监听器中的onApplicationEvent方法
invokeListener(listener, event);
}
}
}
}
4.Spring事件机制改造案例
定义用户注册事件对象
public class UserRegistryEvent extends ApplicationEvent {
private static final long serialVersionUID = 3423209771924745853L;
private User user; //携带数据
public UserRegistryEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() {
return this.user;
}
}
实现事件监听器
public class EmailProcessorListener implements ApplicationListener<UserRegistryEvent> {
public void sendEmail(String userId) {
System.out.println("为" + userId + "用户发送注册成功邮件");
}
@Override
public void onApplicationEvent(UserRegistryEvent event) {
User user = event.getUser();
this.sendEmail(user.getMailbox());
}
}
由于Spring已经实现了事件广播器的实现类SimpleApplicationEventMulticaster直接可以使用,那么在业务类种组合使用即可,如下:
public class UserController {
private UserService userService;
private ApplicationEventMulticaster eventMulticaster;
public UserController(UserService userService, ApplicationEventMulticaster eventMulticaster) {
this.userService = userService;
this.eventMulticaster = eventMulticaster;
this.eventMulticaster.addApplicationListener(new EmailProcessorListener());
}
public void register(User user) {
userService.register(user);
// 为所有观察者发布“注册成功的事件”
eventMulticaster.multicastEvent(new UserRegistryEvent(this.eventMulticaster, user));
}
}
5.事件发布者(ApplicationEventPublisher)
Spring种已经提供了事件广播器,可以发布事件,可是这里又提供了一个事件发布者ApplicationEventPublisher接口,该接口也是用来广播(发布)事件的,可是为什么还要提供它呢?可以看到上面我们是采用硬编码的方式实现的,在eventMulticaster中添加监听器我们是需要执行其addApplicationListener的,而且我们是直接new出来的监听器对象,这样容器就不能管理监听器对象,而且这样使用起来也比较麻烦一些,配置文件中还需要配置SimpleApplicationEventMulticaster信息并注入。为了简化使用,Spring提供了面向接口的方式,出现了这个事件发布者,与他组合的是ApplicationEventPublisherAware接口,我们可以通过setApplicationEventPublisher方法获取ApplicationEventPublisher的实现类对象,那么我们的业务类也就有了发布事件的能力。在实例化Bean之后,Spring会自行调用setApplicationEventPublisher方法将容器本身作为事件发布者传递给我们。(ApplicationContext是该接口的一个子接口。在AbstractApplicationContext中实现了这两个方法,其中真正的事件发布操作也是委托给ApplicationEventMulticaster来做的,当然作为ClassPathXmlApplicationContext也具有发布事件的功能,也是一个事件发布者)。
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
那么我们继续使用接口的方式来修改上面示例,只修改UserController如下:
public class UserController implements ApplicationEventPublisherAware {
private UserService userService;
private ApplicationEventPublisher applicationEventPublisher;
public void register(User user) {
userService.register(user);
applicationEventPublisher.publishEvent(new UserRegistryEvent(this, user));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
//省略getter和setter方法……
}
配置如下:
<bean id="userService" class="com.mec.spring.event.UserService"></bean>
<bean id="user" class="com.mec.spring.event.User">
<property name="mailbox" value="用户123"></property>
</bean>
<bean id="emailProcessorListener" class="com.mec.spring.event.EmailProcessorListener"></bean>
<bean id="userController" class="com.mec.spring.event.UserController">
<property name="userService" ref="userService"></property>
</bean>
Spring容器在创建bean过程中,会判断bean是否为ApplicationListener类型,如果是则将其作为监听器注册到AbstractApplicationContext#applicationEventMulticaster
三、异步模式
在前面SimpleApplicationEventMulticaster这个类中用于广播事件的方法multicastEvent源码中,会先获取当前SimpleApplicationEventMulticaster对象中维护的线程池,如果存在线程池那么就会利用线程池中的存在线程去执行任务(监听器对事件的响应onApplicationEvent方法)。我们一般用的容器都是AbstractApplicationContext类型的,在该类的内部维护了一个ApplicationEventMulticaster类型的成员applicationEventMulticaster,它的初始化方法如下:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster"
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME,ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME,this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
上面的逻辑比较清晰:如果存在一个ApplicationEventMulticaster类型且bean名称为“applicationEventMulticaster”的Bean,则容器内部就会使用这个实例,否则就初始化一个SimpleApplicationEventMulticaster实例(该实例没有设置其Executor属性,为同步模式)。那么我们只要配置一个自己的可以异步执行任务的ApplicationEventMulticaster,然后将其bean名称设置为“applicationEventMulticaster”就行。基于前面的不变,增加如下配置:
<bean id="threadPoolExecutorFactoryBean" class="org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean">
<property name="corePoolSize" value="3"></property>
</bean>
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="threadPoolExecutorFactoryBean"></property>
</bean>