SpringBoot事件监听机制及发布订阅模式详解
业务需求:
用户注册成功之后,系统会给用户发放优惠券,发送邮件,发送短信等操作。
作为开发人员,很容易写出如下代码:
/**
* 用户注册逻辑
*
* @author Lynch
*/
@GetMapping("/register")
public String register(String username) {
// 注册
userService.register(username);
// 发送邮件
emailService.sendEmail(username);
// 发送短信
smsService.sendEmail(username);
// 发放优惠券
couponService.addCoupon(username);
return "注册成功!";
}
这样写会有什么问题?
1. 方法调用时,同步阻塞导致响应变慢,需要异步非阻塞的解决方案。
2. 注册接口此时做的事情:注册,发邮件,发短信,发优惠券,违反单一职责的原则。当然,如果后续没有拓展和修改的需求,这样子倒可以接受。
3. 如果后续注册的需求频繁变更,相应就需要频繁变更register方法,违反了开闭原则。
我们采用Spring事件监听机制解决以上存在的问题。
Spring事件监听机制概述
SpringBoot中事件监听机制则通过发布-订阅实现,主要包括以下三部分:
1. 事件 ApplicationEvent,继承JDK的EventObject,可自定义事件。
2. 事件发布者 ApplicationEventPublisher,负责事件发布
3. 事件监听者 ApplicationListener,继承JDK的EventListener,负责监听指定的事件
我们通过SpringBoot的方式,能够很容易实现事件监听,接下来我们实现下上面的案例:
一、定义注册事件——UserRegisterEvent
package cn.itcast.user.demo.event;
import org.springframework.context.ApplicationEvent;
/**
* 定义注册事件
*
* @author Lynch
*/
public class UserRegisterEvent extends ApplicationEvent {
private String username;
public UserRegisterEvent(Object source) {
super(source);
}
public UserRegisterEvent(Object source, String username) {
super(source);
this.username = username;
}
public String getUsername() {
return username;
}
}
二、注解方式 @EventListener定义监听器——CouponService
package cn.itcast.user.demo.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
/**
* 监听用户注册事件,执行发放优惠券逻辑
*
* @author Lynch
*
*/
@Service
@Slf4j
public class CouponService {
@EventListener
public void addCoupon(UserRegisterEvent event) {
log.info("给用户[{}]发放优惠券", event.getUsername());
}
}
三、注解方式 @EventListener定义监听器——SmsService
package cn.itcast.user.demo.event;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
/**
* 监听用户注册事件,异步执行发送短信逻辑
*
* @author Lynch
*
*/
@Service
@Slf4j
public class SmsService implements ApplicationListener<UserRegisterEvent> {
@Override
@Async("taskExecutor")
public void onApplicationEvent(UserRegisterEvent event) {
log.info("给用户[{}]发送短信", event.getUsername());
}
}
四、实现ApplicationListener的方式定义监听器——EmailService
package cn.itcast.user.demo.event;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
/**
* 监听用户注册事件,异步执行发送邮件逻辑
*
* @author Lynch
*
*/
@Service
@Slf4j
public class EmailService implements ApplicationListener<UserRegisterEvent> {
@Override
@Async("taskExecutor")
public void onApplicationEvent(UserRegisterEvent event) {
log.info("给用户[{}]发送邮件", event.getUsername());
}
}
五、注册事件发布者——UserServiceImpl
package cn.itcast.user.demo.event;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
/**
* 注册事件发布者
*
* @author Lynch
*
*/
@Service
@Slf4j
public class UserServiceImpl implements ApplicationEventPublisherAware {
// 注入事件发布者
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* 发布事件
*/
public void register(String username) {
log.info("执行用户[{}]的注册逻辑", username);
applicationEventPublisher.publishEvent(new UserRegisterEvent(this, username));
}
}
六、单元测试——UserServiceImplTest
package cn.itcast.user.service;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import cn.itcast.user.BaseTest;
import cn.itcast.user.demo.event.UserServiceImpl;
public class UserServiceImplTest extends BaseTest {
@Autowired
private UserServiceImpl userServiceImpl;
@Test
public void register() {
userServiceImpl.register("张三");
}
}
运行结果如下:
11-17 14:39:49:520 INFO 20696 --- [ main] c.i.user.demo.event.UserServiceImpl : 执行用户[张三]的注册逻辑
11-17 14:39:49:522 INFO 20696 --- [ main] cn.itcast.user.demo.event.CouponService : 给用户[张三]发放优惠券
11-17 14:39:49:533 INFO 20696 --- [viceExecutor -1] cn.itcast.user.demo.event.EmailService : 给用户[张三]发送邮件
11-17 14:39:49:534 INFO 20696 --- [viceExecutor -2] cn.itcast.user.demo.event.SmsService : 给用户[张三]发送短信