使用Spring Event解耦业务开发

摘要: 使用Spring Event解耦业务开发

正文:

使用Spring Event解耦业务开发

事件驱动

事件驱动模型通常被理解为观察者模式或者发布-订阅模型

Spring 事件是观察者模式的一种体现,对象间的一对多关系,被观察者发出信号时候会通知监听该事件的观察者;而发布-订阅模型往往需要一个调度中心,如消息队列等

观察者模式

业务场景

  • 用户注册
  • 发短信/确认邮件
  • 送新人优惠券
  • 送抽奖
  • ….

伪代码

主流程

1
2
3
4
5
6
7
8
9
10
11
12
//注册
userMapper.saveUser(user);
//发确认邮件
sendEmail(String email);
//发短信
sendMessage(String mobile);
//送优惠券
sendCoupon(String userId);
//送抽奖机会
sendLottery(String userId);
//各种活动...
send...();

尽管我们方法抽象的很好,但是当这种事件(注册后续操作)越来越多时,主方法就会显得很乱,并且随着业务需求的变化,这个维护起来也很麻烦

改进版

主流程

1
2
3
4
//注册
userMapper.saveUser(user);
//发送注册成功事件
publisher.publishEvent(new UserRegisterEvent(user));

注册事件

1
2
3
4
5
6
7
8
9
10
11
class UserRegisterEvent extends ApplicationEvent {
private static final long serialVersionUID = -4829855648590354032L;

public UserRegisterEvent(User user) {
super(user);
}

public User getUser() {
return (User) source;
}
}

不同监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//发短信监听
class MessageListener implements ApplicationListener<UserRegisterEvent> {
@Override
public void onApplicationEvent(UserRegisterEvent event) {
sendMessage(event.getUser().getMobile());
}
}
//发优惠券监听
class CouponListener implements ApplicationListener<UserRegisterEvent> {
@Override
public void onApplicationEvent(UserRegisterEvent event) {
sendCoupon(event.getUser().getId());
}
}
//...监听

同步 OR 异步

Spring 事件既可以同步又可以异步,对于重要的业务最好采用同步方式,对于不重要的或不希望其阻塞主线程从而导致响应变慢可以采用异步方式

同步(default)

监听会加入到主线程的事务中,可以通过Order来调整bean装配的优先级来实现监听的执行顺序

异步

需要配置线程池来实现,顺序无法保证

综上所述,Spring 事件主要还是对代码层面的解耦

Spring Event 实现细节

Source: 4.2

publisher.publishEvent() 为入口

org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected void publishEvent(Object event, ResolvableType eventType) {
//如果是ApplicationEvent类型对象
if (event instanceof ApplicationEvent类型对象) {
applicationEvent = (ApplicationEvent) event;
}
else {
//否则就new一个PayloadApplicationEvent
applicationEvent = new PayloadApplicationEvent<Object>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
}

// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
//实际执行是委托给ApplicationEventMulticaster
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
}

同步or 异步

org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}

初始化

org.springframework.context.support.AbstractApplicationContext#refresh

1
2
3
4
5
6
@Override
public void refresh() throws BeansException, IllegalStateException {
...
initApplicationEventMulticaster();
...
}

org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster

1
2
3
4
5
6
7
8
9
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
//手动实现了名称为applicationEventMulticaster的bean
}
else {
//否则就默认自定义一个名称为SimpleApplicationEventMulticaster的监听器容器
}
}

Demo

Spring-Event-Demo