如何实现业务解耦?spring中事件监听了解一下

简介: 耦合这个词在平常的开发工作中应该不陌生,简单理解就是代码中各部分关联度过高。

一、前言


   耦合这个词在平常的开发工作中应该不陌生,简单理解就是代码中各部分关联度过高。举一个大家都遇见过的经典耦合场景:用户注册成功之后需要进行发送短信通知或是邮件通知,用户注册逻辑与发送短信或是邮件通知逻辑放在一块就是一种耦合现象,如果短信或是邮件功能异常,整个用户注册功能就会异常,会带来不好的用户体验,另外的缺点是维护复杂,不便于拓展。将业务逻辑与功能性逻辑进行拆分开就是属于解耦。spring中IOC思想就是解耦的一种体现,毕竟在一个实现类中如果调用另一个实现类不用繁琐的创建对象,只需要把需要用到实现类进行注入就可以了,减少代码量的同时也便于后期的拓展与维护。本文主要解决的问题就是在平常的开发中,如何将业务代码与固定功能性代码进行剥离,就是如何实现业务解耦。本文介绍一种解耦的实现思路:spring事件监听实现解耦。


二、业务解耦之事件监听实战


1.事件监听三大组件

   关于事件监听大家都听过,现在重新认识一下事件监听的三个重要部分:

   1.1 事件源

   可以理解为事件类型。比如说我们将的发送邮件或是发送短信都算是一种事件类型,都可以叫做事件源。

   1.2 事件发布器

   事件发布器也可以称作事件发布者,主要作用是发布事件,一般用于业务逻辑完成之后进行事件发布。

   1.3 事件监听器

   主要作用就是监听是否有事件发布,如果有则执行对应的业务处理。

三者之间的关系流程如下:

bc651fcefa7eed8ee64b4b3a1cead88a_07775b8586c04a1ba647938187a173d9.png


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();
    }
}

约课课程接口发送成功之后实现结果显示:

1a39a9bb032885ee0d495b988aed4d86_0d09d6367e714c82992d37e3b3674089.png


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接口.

   以上是实现业务解耦采用事件监听的处理方式并对实现原理做了必要的分析说明,看到如果感觉有收获欢迎点赞收藏,另外也可以在评论区留言说下其他的业务解耦处理方式.


相关文章
|
4月前
|
XML Java 数据格式
[Spring 基础] 掌握Spring DI,轻松解耦你的应用
[Spring 基础] 掌握Spring DI,轻松解耦你的应用
|
7月前
|
设计模式 Java uml
彻底搞懂Spring状态机原理,实现订单与物流解耦
状态模式的UML类图如下图所示。
153 0
|
10月前
|
XML 人工智能 Java
Spring中基于xml的IOC解耦
Spring中基于xml的IOC解耦
|
10月前
|
SQL 人工智能 Java
Spring中使用工厂模式解耦详解
Spring中使用工厂模式解耦详解
|
10月前
|
消息中间件 Java Spring
下单流程解耦新方案-你知道Spring事件监听机制吗
下单流程解耦新方案-你知道Spring事件监听机制吗
74 0
|
11月前
|
缓存 Java Go
Spring5源码 - 13 Spring事件监听机制_@EventListener源码解析
Spring5源码 - 13 Spring事件监听机制_@EventListener源码解析
96 0
|
11月前
|
Java Spring 容器
Spring5源码 - 12 Spring事件监听机制_异步事件监听应用及源码解析
Spring5源码 - 12 Spring事件监听机制_异步事件监听应用及源码解析
103 0
|
11月前
|
前端开发 Java Spring
Spring MVC-02循序渐进之解耦控制器和校验器
Spring MVC-02循序渐进之解耦控制器和校验器
59 0
|
运维 Java 数据库连接
两种方式实现Spring 业务验证
验证在任何时候都非常关键。考虑将数据验证作为业务逻辑开发有利也有弊,Spring 认为,验证不应该只在Web 端进行处理,在服务端也要进行相应的处理,可以防止脏数据存入数据库中,从而避免为运维同学和测试同学造成更大的困扰,因为数据造成的bug会更加难以发现,而且开发人员关注点也不会放在数据本身的问题上,所以做服务端的验证也是非常有必要的。考虑到上面这些问题,Spring 提供了两种主要类型的验证:
292 0
|
18天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
37 0