思考集结处

vuePress-theme-reco 思考集结处    2024
思考集结处 思考集结处

Choose mode

  • dark
  • auto
  • light
首页
标签
分类
  • AI
  • Docker
  • 分布式事务
  • 文件存储
  • 框架
  • Spring
  • java
  • 其他
  • 搜索引擎
  • 源码
  • 网站
Java
网站
容器技术
搜索引擎
分布式事务
源码系列
框架系列
文件存储
AI
其他
GitHub
author-avatar

思考集结处

43

文章

18

标签

首页
标签
分类
  • AI
  • Docker
  • 分布式事务
  • 文件存储
  • 框架
  • Spring
  • java
  • 其他
  • 搜索引擎
  • 源码
  • 网站
Java
网站
容器技术
搜索引擎
分布式事务
源码系列
框架系列
文件存储
AI
其他
GitHub
  • Java相关技术
  • 网络编程相关概念
  • 同步阻塞IO模型
  • 同步非阻塞IO模型
  • 多路复用IO模型
  • 异步IO模型
  • 多线程源码分析
  • 线程池
  • Spring 事件监听
  • Spring 重试机制
  • Spring 重试机制之listeners参数

Spring 事件监听

vuePress-theme-reco 思考集结处    2024

Spring 事件监听

思考集结处 2023-07-16 事件监听

Spring 的事件监听在开发中能为我们做那些事情?

# 背景

大家在做项目的时候,往往会遇到一些不是主流程的功能,但是需要在主流程中触发的功能,比如在保险领域的签单流程中需要给用户发送短信或者邮件的功能。

# 分析

我们都知道这些非主流程中功能,最好不要让主线程去执行,这个时候我们能想到的就是异步线程去执行,我们可以单独搞个线程池,然后异步处理这些任务。 但是总是感觉不太优雅。这时我们可以结合Spring的事件机制加上异步线程去实现这些功能。

# 实现

我们假定现在你有一个后台管理系统,涉及到登陆和注册两个场景,在注册和登录成功后给用户发送短信邮件提醒或者其他的功能。

# 创建工程

我们为了测试方便就直接创建一个简单的Spring Boot的工程就可以了。创建的时候引入如下依赖:

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
  </dependency>
1
2
3
4
5
6
7
8
9

# 创建事件接口

为了定义统一事件行为,我们定义一个事件接口,代码如下:

public interface Event {
    /**
     * 发送短信
     */
    void sendMsg();

    /**
     * 发送邮件
     */
    void sendMail();

    /**
     * 其他行为
     */
    void other();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 创建登录事件

创建一个登陆事件类,并且实现事件接口。代码如下:

@Data
@Slf4j
public class LoginEvent implements Event {
    /**
     * 登录的姓名
     */
    private String  name;

    public LoginEvent(String name) {
        this.name = name;
    }

    @Override
    public void sendMsg() {
      log.info("登录短信内容为:你好:{},欢迎登录该系统",name);
    }

    @Override
    public void sendMail() {
        log.info("登录邮件内容为:你好:{},欢迎登录该系统",name);
    }

    @Override
    public void other() {
        log.info("登录其他内容为:你好:{},欢迎登录该系统",name);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

说明:为什么我们要创建一个接口呢?其实是为了使用策略模式,最大程度的复用事件监听主要代码。

# 创建注册事件

创建一个登陆事件类,并且实现事件接口。代码如下:

@Data
@Slf4j
public class RegisterEvent implements Event  {
    /**
     * 注册人的姓名
     */
    private String  name;

    public RegisterEvent(String name) {
        this.name = name;
    }

    @Override
    public void sendMsg() {
        log.info("注册短信内容为:你好:{},欢迎注册该系统",name);
    }

    @Override
    public void sendMail() {
        log.info("注册邮件内容为:你好:{},欢迎注册该系统",name);
    }

    @Override
    public void other() {
        log.info("注册其他内容为:你好:{},欢迎注册该系统",name);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 创建事件监听

大家注意,事件监听代码的入参是事件监听的接口,这就是为什么要创建一个接口的原因。

注解解释: @EventListener-代表是一个监听器 @Async-这个方法需要异步执行

@Component
@Slf4j
public class EventsListener {
    /**
     * 事件监听
     * @param event
     */
    @Async
    @EventListener
    public void handelListener(Event event){
        event.sendMsg();
        event.sendMail();
        event.other();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 测试

我们创建测试类,并且用postman进行测试

# 创建测试类

我们创建一个控制器来测试登陆和注册的功能。代码如下:

@RestController
public class EventController {

    @Resource
    LoginService loginService;
    @Resource
    private ApplicationEventPublisher applicationContext;

    @GetMapping("/login")
    private String testLoginEvent(){
        //登陆的逻辑
        loginService.login("张三");
        //登陆完成后发布登陆后的事件
        applicationContext.publishEvent(new LoginEvent("张三"));
        return "success";
    }

    @GetMapping("/register")
    private String testRegisterEvent(){
        applicationContext.publishEvent(new RegisterEvent("张三"));
        return "success";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 测试

我们Sping Boot项目,并且进行测试,来看一下效果。

使用postman请求我们的login方法,我们观察一下日志。请求地址localhost:8111/login,日志如下:

2023-07-16 17:25:16.774  INFO 7070 --- [nio-8111-exec-1] o.t.s.e.service.impl.LoginServiceImpl    : 欢迎:张三,登陆系统。
2023-07-16 17:25:16.795  INFO 7070 --- [         task-1] o.t.s.eventlistener.event.LoginEvent     : 登录短信内容为:你好:张三,欢迎登录该系统
2023-07-16 17:25:16.795  INFO 7070 --- [         task-1] o.t.s.eventlistener.event.LoginEvent     : 登录邮件内容为:你好:张三,欢迎登录该系统
2023-07-16 17:25:16.795  INFO 7070 --- [         task-1] o.t.s.eventlistener.event.LoginEvent     : 登录其他内容为:你好:张三,欢迎登录该系统
1
2
3
4

可以看到,我们的请求的线程是[nio-8111-exec-1],事件监听的执行是异步线程[task-1],这样就实现了我们文章开头所说的功能。

# 说明

大家在使用Spring Boot的过程中使用到@Async注解时记得开启该注解的使用。代码如下:

@SpringBootApplication
@EnableAsync
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }

}
1
2
3
4
5
6
7
8
9

# 小结

本篇文章,我们简单的在实现层面整理了使用Spring的事件监听机制实现异步功能,后续有机会带着大家分析一下这部分源码,详细代码,大家可以直接进行下载使用源码地址 , 希望对大家有所帮助。

我是思考集结处欢迎你的关注
看板娘