阅读spring源码时,看到ApplicationEvent
相关的代码觉得熟悉又困惑,深入了解了一下,发现原来是spring事件机制(原谅我之前没用过……)。
这里在【Spring4揭秘 基础1】监听器和事件的基础下进行一下扩展深入,感谢这篇博文的作者,他的spring基础系列文章让我在阅读源码时,轻松了不少。
注:源码部分根据spring-5.0.7版本分析
设计模式
spring事件机制其实就是观察者模式的一种体现。忘记或不熟悉观察者模式的朋友可以看我前面的总结:Head First 设计模式(二)观察者模式
观察者模式简单可分为两部分:主题和观察者。当一个主题改变状态时,它的所有依赖者都会收到通知并进行自动更新。
Spring事件机制简单可分为三部分:事件、广播、观察者。 “主题改变状态” 这个动作被抽离成了 一个“事件”,由一个持有所有观察者的“广播容器” 进行广播,“观察者”们 接收到相应事件后进行自动更新。
这种设计其实继承自Java本身的事件机制:
java.util.EventObject
事件状态对象的基类,它封装了事件源对象以及和事件相关的信息。所有java的事件类都需要继承该类。java.util.EventListener
观察者基类,当事件源的属性或状态改变的时候,调用相应观察者内的回调方法。Source
主题类,java中未定义,持有所有的观察者,当主题状态发生改变,产生事件,负责向所有观察者发布事件
Java的事件机制这里不敞开讲,想了解可看:java 事件机制
Spring中的事件机制
Spring的事件机制相关的核心类有四个:
-
ApplicationEvent
: Spring中的事件基类,继承自java.util.EventObject
,创建是需要指定事件源
public abstract class ApplicationEvent extends EventObject {
/**
* 创建一个事件,需要指定事件源
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
}
-
ApplicationEventPublisher
:发布事件者,调用广播发布事件
public interface ApplicationEventPublisher {
/**发布事件*/
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
-
ApplicationEventMulticaster
:广播,持有观察者集合,可向集合内的所有观察者通知事件
public interface ApplicationEventMulticaster {
/**
* 添加监听者(观察者)
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* 删除监听者(观察者)
*/
void removeApplicationListener(ApplicationListener<?> listener);
/**
* 向所有监听者发布事件
*/
void multicastEvent(ApplicationEvent event);
}
-
ApplicationListener
:观察者,接收对应事件后,执行逻辑
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* 接收事件后,执行相应逻辑
*/
void onApplicationEvent(E event);
}
事件发布者ApplicationEventPublisher
持有广播,而广播ApplicationEventMulticaster
持有若干观察者ApplicationListener
。一个事件ApplicationEvent
可以通过发布者ApplicationEventPublisher
发布后,会调用广播ApplicationEventMulticaster
通知所有观察者,观察者ApplicationListener
收到通知后执行相关操作。
下面举例说明:
当一个用户注册结束后,我们想要将这个事件发生给短信监听者和邮件监听者,让他们向用户发送短信和邮件。
public class EventDemo {
public static void main(String[] args) {
//构建广播器
ApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
//广播添加监听器
multicaster.addApplicationListener(new RegisterListener1());
multicaster.addApplicationListener(new RegisterListener2());
//构建事件发布者
MyEventPublicsher eventPublicsher = new MyEventPublicsher();
//事件发布者增加广播
eventPublicsher.setEventMulticaster(multicaster);
//构建注册事件
User user = new User("jack", "18782252509", "jack_email@163.com");
System.out.println("用户注册……");
RegisterEvent registerEvent = new RegisterEvent(user);
//发布注册事件
eventPublicsher.publishEvent(registerEvent);
}
/**
* 用户实体类
*/
public static class User{
private String id;
private String name;
private String phone;
private String email;
public User(String name, String phone, String email) {
this.name = name;
this.phone = phone;
this.email = email;
}
//.....GET AND SET
}
/**
* 自定义注册事件
*/
public static class RegisterEvent extends ApplicationEvent {
//事件的构造方法中,必须制定事件源
public RegisterEvent(User user) {
super(user);
}
public User getUser(){
return (User) getSource();
}
}
/**
* 注册事件监听者1-短信监听者(即观察者),负责注册后发生短信
* 注意:实现接口时,在泛形中指定事件类型,则只监听该类型事件。若不指定,则默认监听所有事件。
*/
public static class RegisterListener1 implements ApplicationListener<RegisterEvent> {
public void onApplicationEvent(RegisterEvent event) {
User user = event.getUser();
System.out.println("用户:"+ user.getName()+"注册结束,向手机"+user.getPhone()+"发送短信!");
}
}
/**
* 注册事件监听者2-邮件监听者(即观察者),负责注册后发送邮件
* 注意:实现接口时,在泛形中指定事件类型,则只监听该类型事件。若不指定,则默认监听所有事件。
*/
public static class RegisterListener2 implements ApplicationListener<RegisterEvent> {
public void onApplicationEvent(RegisterEvent event) {
User user = event.getUser();
System.out.println("用户:"+ user.getName()+"注册结束,发生邮件:"+user.getEmail());
}
}
/**
* 事件发布者,持有监听者
*/
public static class MyEventPublicsher implements ApplicationEventPublisher{
//广播
private ApplicationEventMulticaster eventMulticaster;
public void setEventMulticaster(ApplicationEventMulticaster eventMulticaster) {
this.eventMulticaster = eventMulticaster;
}
//发布事件
public void publishEvent(Object event) {
eventMulticaster.multicastEvent((ApplicationEvent) event);
}
}
}
输出:
用户注册后
用户:jack注册结束,向手机18782252509发送短信!
用户:jack注册结束,发生邮件:jack_email@163.com
源码细节解析
我们主要分析下广播的细节,以SimpleApplicationEventMulticaster
为例:
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
//事件被统一封装成了ResolvableType,方便形参入口统一
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//根据事件类型,通过泛形反射获取对应的监听者
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//获取广播配置的线程池
Executor executor = getTaskExecutor();
//如果有配置线程池,则异步通知事件监听者
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
//没有配置线程池,同步通知事件监听者
else {
invokeListener(listener, event);
}
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//执行监听者对应逻辑
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
……
}
}
}
我们可以看到spring广播时,会先去判断有没有配置线程池,如果配置则使用线程池异步执行监听者逻辑,否则同步。
需要注意的是,我们使用spring事件机制时,默认是没有配置线程池的,也就是默认所有的通知都是同步的,需要手动指定线程池才会开启同步。
应用
设计一个业务场景:当一个用户完成贷款订单后,我们希望执行发送提醒短信、调用积分服务增加积分、通知风控服务重算风控值(后续操作可能增加)等功能。这种业务需求开始很可能写成同步代码。
//创建订单
public void createOrder(Order order){
创建贷款订单;
发送提醒短信;
调用积分服务增加积分;
调用风控服务推送订单信息;
……
返回;
}
随着业务复杂度的增加,我们很快发现createOrder()
这个方法耦合了太多与创建订单无关的逻辑,即影响了原本创建订单方法的效率,在设计上又不符合“开闭原则”。
现在使用spring事件机制我们来解耦,将与注册无关的操作改为异步。这里直接使用注解式写法。
- 首先我们修改spring中的广播,为它注入我们自定义的线程池,在spring配置加上:
<!--自定义线程池-->
<bean id="myExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
<!--修改容器中的广播,注入自定义线程池-->
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="myExecutor" />
</bean>
- 定义一个创建订单事件
/**
* 创建订单完成事件
*/
@Component
public class AfterCreateOrderEvent extends ApplicationEvent {
public AfterCreateOrderEvent(Order order) {
super(order);
}
public Order getOrder(){
return (Order) getSource();
}
}
- 使用事件机制改变原有代码
@Service
public class OrderService {
//直接注入spring事件发布者
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
/**
* 简单的创建订单方法
*/
public void createOrder(Order order) throws InterruptedException {
System.out.println("创建订单 order:"+order.getOrderNo()+" 结束");
//调用事件发布者发布事件
applicationEventPublisher.publishEvent(new AfterCreateOrderEvent(order));
System.out.println("createOrder方法 结束");
}
//加入@EventListener注解后,该方法可以看出一个事件监听者
@EventListener
public void afterCreateOrder(AfterCreateOrderEvent afterCreateOrderEvent) throws InterruptedException {
Order order = afterCreateOrderEvent.getOrder();
Thread.sleep(2000);
System.out.println("调用短信通知服务:" + order.getPhone());
System.out.println("调用积分服务增加贷款积分:"+order.getOrderNo());
}
public static void main(String[] args) throws InterruptedException {
Order order = new Order("N123124124124", "18782202534");
//这里指定自己的spring配置路径
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config/spring-config.xml");
OrderService orderService = context.getBean(OrderService.class);
orderService.createOrder(order);
}
}
输出:
创建订单 order:N123124124124 结束
createOrder方法 结束
调用短信通知服务:18782202534
调用积分服务增加贷款积分:N123124124124
自此,创建订单与其他操作便实现了异步和解耦。
另一种异步实现方式
另外,也可使用@Async注解来实现事件的异步调用
@EventListener
@Async
public void afterCreateOrder(AfterCreateOrderEvent afterCreateOrderEvent) throws InterruptedException {
Order order = afterCreateOrderEvent.getOrder();
Thread.sleep(2000);
System.out.println("调用短信通知服务:" + order.getPhone());
System.out.println("调用积分服务增加贷款积分:"+order.getOrderNo());
}
spring配置加上:
<!--开启异步调用,并指定线程池-->
<task:annotation-driven executor="annotationExecutor" />
<!--线程池-->
<task:executor id="annotationExecutor" pool-size="20"/>
但这种方法有弊端,afterCreateOrder()
方法不能放在同一类(OrderService
)里面。原因是spring的代理机制。