业务重构时用事件驱动模式
作者:互联网
前言
需求:当新用户注册时,需要给用户发放各种礼品、积分、短信、邀请人奖励等。
常见写法
-
直接将上述后续操作堆到注册方法里,搞的注册方法又臭又长;当有其他注册如app注册、小程序注册、第三方注册时,然后将同样的发放逻辑复制的到处都是,可读性、维护性极差
-
优化:可以将发放逻辑抽成一个方法,然后供调用,这个应该是最常用的;
但随着业务的扩张,可能不止普通用户、还有承包商、供应商等。可能都已经是不同的表了,他们也有不同的发放逻辑。
然后发放逻辑建好几个方法,将之前的逻辑复制过去改一改;最后这个发放逻辑就会变得很臃肿难以维护。
-
再优化:可以套用事件驱动模式肢解该逻辑,当然也可以不使用。一切都要从业务出发,业务不复杂就完全没必要使用。因为不管用到什么设计模式都会建好多个类,虽然在单个类里维护性很强,但找起来可能不方便、整体使用还是不如直接调用方便。
就是说不能为了使用设计模式而使用设计模式
设计模式优势
- 一般来说使用设计模式可以提高代码的三性:
- 提高代码的可重用性
- 提高代码的可读性
- 提高代码的可靠性
事件驱动模式处理
- 事件源:用户
- 事件:用户注册
- 事件监听器:监听到用户注册时调用奖励发放功能
步骤
1. 建UserRegisterEvent类继承ApplicationEvent
/**
* 用户注册事件
*
* @author MinWeikai
* @date 2021-11-01 17:42:33
*/
public class UserRegisterEvent extends ApplicationEvent {
private User user;
public UserRegisterEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
2. 用户注册事件发布
/**
* 用户注册事件发布
*
* @author MinWeikai
* @date 2021-11-01 17:45:06
*/
@Component
public class UserRegisterEventPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void publish(User user) {
this.applicationEventPublisher.publishEvent(new UserRegisterEvent(this, user));
}
}
3. 用户注册事件监听
/**
* 用户注册事件监听
*
* @author MinWeikai
* @date 2021-11-01 17:43:09
*/
@Slf4j
@Component
public class UserRegisterEventListener implements ApplicationListener<UserRegisterEvent> {
@SneakyThrows
@Override
public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
User user = userRegisterEvent.getUser();
log.info("用户:[{}] 进行了注册,开始发放奖励", user.getName());
Thread.sleep(1000);
log.info("执行发放奖品");
Thread.sleep(1000);
log.info("执行发放积分");
Thread.sleep(1000);
log.info("执行发送短信");
Thread.sleep(1000);
log.info("新用户奖励发放完成");
}
}
4. 测试执行
/**
* @author MinWeikai
* @date 2021-11-01 17:45:43
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRegisterEventPublisherTest {
@Autowired
private UserRegisterEventPublisher userRegisterEventPublisher;
@Test
public void publish() throws InterruptedException {
User user = new User();
user.setName("君莫笑");
userRegisterEventPublisher.publish(user);
Thread.sleep(1000*10);
}
}
5. 当有其他监听者时,如邀请人奖励发放
/**
* 用户注册事件邀请人监听
*
* @author MinWeikai
* @date 2021-11-01 17:43:09
*/
@Slf4j
@Component
public class UserRegisterEventInviterListener implements ApplicationListener<UserRegisterEvent> {
@SneakyThrows
@Override
public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
User user = userRegisterEvent.getUser();
log.info("用户:[{}] 进行了注册,开始发放邀请人奖励", user.getName());
Thread.sleep(1000);
log.info("邀请人奖励发放完成");
}
}
监听同一事件对象,业务代码相对解耦
原理
-
点这个方法进去applicationEventPublisher.publishEvent
protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); // Decorate event as an ApplicationEvent if necessary ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; } else { applicationEvent = new PayloadApplicationEvent<>(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 { // !! 主要看这里面 !! getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // Publish event via parent context as well... if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } }
-
获取事件的监听器执行
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); // 如果初始化异步任务,就会异步执行每个监听器,否则就是同步执行 Executor executor = getTaskExecutor(); // 此处根据事件对象获取它的所有监听器 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
-
执行UserRegisterEventListener实现的接口
@SuppressWarnings({"rawtypes", "unchecked"}) private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { // 最后就是这里去执行UserRegisterEventListener实现的接口 listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isTraceEnabled()) { logger.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
异步
1. 怎么异步使用监听器
可以自己注入一个SimpleApplicationEventMulticaster类,给里面设置上线程即可
/**
* 自己注入SimpleApplicationEventMulticaster,设置执行线程
*
* @author MinWeikai
* @date 2021/11/3 10:35
*/
@Configuration
public class MySimpleApplicationEventMulticaster {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setAllowCoreThreadTimeOut(true);
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(20);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
taskExecutor.setThreadFactory(new CustomizableThreadFactory("MySimpleApplicationEventMulticaster-pool-"));
return taskExecutor;
}
@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster(@Qualifier("taskExecutor") ThreadPoolTaskExecutor taskExecutor) {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(taskExecutor);
return multicaster;
}
}
总结
优点
- 从以上步骤中可以看到事件对象、事件发布、事件监听器代码相对解耦
- 当有新的业务时如邀请人事件监听,只需要添加一个事件监听器即可,无需修改原有代码。减少对现有业务的影响,相对扩展性较好
- 可以更容易开发和维护不可预知的服务或异步服务
缺点
- 同一事件的监听器尽量放在同一目录,有可能会出现同一事件的不同监听器散落在不同的模块中,增加业务逻辑的复杂度
代码
标签:重构,用户注册,时用,event,事件驱动,user,监听器,taskExecutor,public 来源: https://blog.csdn.net/weixin_41187876/article/details/121176472