一、前言
耦合这个词在平常的开发工作中应该不陌生,简单理解就是代码中各部分关联度过高。举一个大家都遇见过的经典耦合场景:用户注册成功之后需要进行发送短信通知或是邮件通知,用户注册逻辑与发送短信或是邮件通知逻辑放在一块就是一种耦合现象,如果短信或是邮件功能异常,整个用户注册功能就会异常,会带来不好的用户体验,另外的缺点是维护复杂,不便于拓展。将业务逻辑与功能性逻辑进行拆分开就是属于解耦。spring中IOC思想就是解耦的一种体现,毕竟在一个实现类中如果调用另一个实现类不用繁琐的创建对象,只需要把需要用到实现类进行注入就可以了,减少代码量的同时也便于后期的拓展与维护。本文主要解决的问题就是在平常的开发中,如何将业务代码与固定功能性代码进行剥离,就是如何实现业务解耦。本文介绍一种解耦的实现思路:spring事件监听实现解耦。
二、业务解耦之事件监听实战
1.事件监听三大组件
关于事件监听大家都听过,现在重新认识一下事件监听的三个重要部分:
1.1 事件源
可以理解为事件类型。比如说我们将的发送邮件或是发送短信都算是一种事件类型,都可以叫做事件源。
1.2 事件发布器
事件发布器也可以称作事件发布者,主要作用是发布事件,一般用于业务逻辑完成之后进行事件发布。
1.3 事件监听器
主要作用就是监听是否有事件发布,如果有则执行对应的业务处理。
三者之间的关系流程如下:
2.事件发布以及监听实战案例
下面以预约课程成功之后需要发送消息提醒为例讲解一下如何利用监听器实现业务解耦。
首先定义事件源,需要继承spring中事件的顶级父类,实现如下:
public class NewsEvent extends ApplicationEvent { // 消息发送具体内容 private String newsInfo; public NewsEvent(String newsInfo) { super(newsInfo); this.newsInfo=newsInfo; } }
定义事件发布器,这里有两种实现方式,分别是使用实现ApplicationEventPublisherAware.java或是实现ApplicationContextAware.java.
自定义事件发布器实现ApplicationEventPublisherAware.java接口:
@Service @Slf4j public class TestServiceImpl implements TestService,ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher=applicationEventPublisher; } @Override public void applyCourse() { log.info("课程预约成功"); // 消息发送事件发布 NewsEvent sendNewsEvent = new NewsEvent("课程预约成功发送消息事件"); applicationEventPublisher.publishEvent(sendNewsEvent); } }
自定义事件发布器实现ApplicationContextAware .java接口:
@Service @Slf4j public class TestServiceImpl implements TestService,ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext=applicationContext; } @Override public void applyCourse() { log.info("课程预约成功"); // 消息发送事件发布 NewsEvent sendNewsEvent = new NewsEvent("课程预约成功发送消息事件"); applicationContext.publishEvent(sendNewsEvent); } }
最后定义事件监听器,实现方式也有两种,一种是实现ApplicationListener接口,实现逻辑如下:
@Slf4j @Component public class NewsListener implements ApplicationListener<NewsEvent> { @Override public void onApplicationEvent(NewsEvent event) { log.info("消息监听器监听到消息,执行发送消息逻辑:"+event.getSource()); } }
另一种是使用@EventListener注解,使用方式是直接在方法上添加该注解即可,具体实现如下:
@Slf4j @Component public class NewsListener { @EventListener(NewsEvent.class) public void sendNews(){ log.info("消息监听器监听到消息,执行发送消息逻辑"); } }
控制层实现逻辑:
@RestController @RequestMapping("/test") public class TestController { @Autowired private TestServiceImpl testService; @PostMapping("/applyCourse") public ResultVo applyCourse(){ // 预约课程 testService.applyCourse(); return ResultVoUtil.success(); } }
约课课程接口发送成功之后实现结果显示:
3.事件发布以及监听原理
凡事都讲究知其然知其所以然,对我们技术开发人员更是如此,思考几个问题。 事件发布者发送消息之后,监听器是如何立即接收到响应的呢?下面就从源码的角度看下执行原理: 实现类中执行发送事件逻辑,最终调用的是`AbstractApplicationContext`中的`publishEvent`,该方法的主要作用是将所给到的事件发送给所有的监听器.代码如下:
protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); ApplicationEvent applicationEvent; // 省略部分代码 // 利用事件发布器进行发布事件 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); // 省略部分代码 }
继续看下事件发布的具体逻辑,SimpleApplicationEventMulticaster中的multicastEvent:
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); } } }
最终都是执行SimpleApplicationEventMulticaster中doInvokeListener,实现逻辑如下:
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { // 省略部分逻辑 listener.onApplicationEvent(event); // 省略部分逻辑 }
看到这里应该很明确了,这里就说明了为什么自定义的监听器需要实现ApplicationListener接口.
以上是实现业务解耦采用事件监听的处理方式并对实现原理做了必要的分析说明,看到如果感觉有收获欢迎点赞收藏,另外也可以在评论区留言说下其他的业务解耦处理方式.