采用事件监听的好处

以用户注册的业务逻辑为例,用户在填写完信息表单后,提交信息到后台,后台对用户信息进行处理,然后给用户返回处理结果的信息。

如上图所示,用户在注册时,后台需要处理一些系列流程,实际业务逻辑可能更加复杂。这样写很直观,但是不利于后期新业务逻辑的添加。

如果采用事件监听的模式,上面的流程就可以变成如下:

用户在注册的过程中,发送一个信号给监听对象,而这个信号就是用户正在注册的事件,监听对象在收到信号时,就会在后台处理这些流程,如果采用异步事件处理的方式,用户的主干逻辑可以快速完成,而且如果后期需要在注册流程中加入新的逻辑也只需要在监听对象处理事件的过程中加入新的逻辑。

实际代码演示

首先是项目结构,如下

  • controller 处理用户注册请求
  • service    处理用户注册逻辑
  • event      存放事件对象
  • listener   在event的子目录下 监听并调用逻辑处理事件
  • config     存放配置文件

原本还应该有entity和repository层,为简化逻辑,暂时不加

非异步事件处理

编写用户注册事件类,放在event包内,这个类可以根据需求添加一些属性,而这些属性就是代表发生这件事的基本信息。可以通俗的理解,要知道一件事,前提肯定要知道是什么事情。我在这里添加了属性username,表示用户名。

public class UserRegisterEvent extends ApplicationEvent {

    private String userName;

    public UserRegisterEvent(Object source, String userName) {
super(source);
this.userName = userName;
} public void setUserName(String userName) {
this.userName = userName;
} public String getUserName() { return userName;
}
}

编写listener事件监听,用来处理这些事情。当用户注册时,先保存用户注册的信息到数据库,然后给用户发送邮件(本次简化了逻辑,只在控制台打印了信息)。需要在事件处理器上面加上注解@EventListener,一旦事件UserRegisterEvent被发布,监听器就会监听这个事件,然后处理,处理的方式是:保存用户信息,给用户发送邮件。(为了模拟发送邮件这个较为复杂而且耗时的事情,我让线程暂停了5秒,然后继续执行)

@Component
public class UserListener { @EventListener
public void handleUserRegisterEvent(UserRegisterEvent userRegisterEvent) { // save user information
System.out.println(Calendar.getInstance().getTime() + " " + "user information " + userRegisterEvent.getUserName()); // send email to user
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Calendar.getInstance().getTime() + " " + "send Email to " + userRegisterEvent.getUserName());
} }

编写controller层,模拟用户注册,当用户访问localhost:8080/register/{username}时,就算用户注册,注册成功后给用户返回一个hello

@RestController
public class UserController { @Autowired
private UserService userService; @GetMapping("/register/{username}")
public ResponseEntity<String> register(@PathVariable(name = "username") String username){
return ResponseEntity.ok(userService.register(username));
}
}

编写service层,在UserService返回用户注册成功的信息时,先要发布事件,需用ApplicationEventPulisher接口中的pulishEvent( )方法,这个方法的参数就是我们前面创建的UserRegisterEvent的一个对象(注意往对象中存放一些用户基本信息,便于处理器通过这些信息处理事件),当程序运行到这里时就相当于发出一个信号:某某用户正在注册!

@Service
public class UserService { @Autowired
ApplicationEventPublisher publisher; public String register(String username) {
publisher.publishEvent(new UserRegisterEvent(this, username));
return "hello " + username + " " + Calendar.getInstance().getTime();
} }

启动应用程序,访问localhost:8080/register/tom,等待一段时间后就可以看到返回的注册成功信息hello tom,控制台也可以看到用户注册的时一些流程也执行了。

                             

这样就完成了事件监听处理业务逻辑。

异步事件的处理

通过上面的例子已经完成了事件监听,但是也存在一些问题,例如假如发送邮件比较耗时,例如上面的例子,花了5秒钟,那么用户就需要等待五秒,才可以接受到返回的信息。看上面的例子,后台保存用户信息时间是18时38分47秒,邮件发送完毕是18时38分52秒,等到用户接到到信息时已经是18时38分52秒了,这样用户体验显然很差。采用异步事件的方式,新建一个线程,将处理事件放到新的线程中,这样就可以显著提高用户的体验。

要变成异步事件处理,在上面的例子上稍做修改即可:

在UserListener中,在需要做异步处理的方法上加上注解@Async

然后在项目启动类上加上注解@EnableAsync

只要加上这两个注解就可以了。

再次启动项目,访问localhost:8080/register/john,结果如下

    

可以看到用户很快就获得了响应。

补充:如果想自己控制线程,可以在config文件夹下加入配置类,代码如下

public class ListenerAsyncConfiguration implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
//使用Spring内置线程池任务对象
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//设置线程池参数
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
taskExecutor.initialize();
return taskExecutor; } @Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}

总结:采用事件监听的方式可以很轻松实现业务逻辑的解耦,方便后期业务逻辑的扩展。

Spring Boot学习笔记:ApplicationEvent和ApplicationEventListener事件监听的更多相关文章

  1. Cocos2dx 3.1.1 学习笔记整理(4):事件监听与Action的初步使用

    项目忙,趁着刚才有点空,看了下触摸事件在新版本中怎么实现,遇到问题都是去:cocos2d-x-3.1.1\tests\cpp-tests\Classes下面找的,里面都是一些小例子. 首先新的CCNo ...

  2. Spring Boot学习笔记2——基本使用之最佳实践[z]

    前言 在上一篇文章Spring Boot 学习笔记1——初体验之3分钟启动你的Web应用已经对Spring Boot的基本体系与基本使用进行了学习,本文主要目的是更加进一步的来说明对于Spring B ...

  3. Spring Boot 学习笔记(六) 整合 RESTful 参数传递

    Spring Boot 学习笔记 源码地址 Spring Boot 学习笔记(一) hello world Spring Boot 学习笔记(二) 整合 log4j2 Spring Boot 学习笔记 ...

  4. Spring Boot 学习笔记一(SpringBoot启动过程)

    SpringBoot启动 Spring Boot通常有一个名为*Application的入口类,在入口类里有一个main方法,这个main方法其实就是一个标准的java应用的入口方法. 在main方法 ...

  5. Halo 开源项目学习(六):事件监听机制

    基本介绍 Halo 项目中,当用户或博主执行某些操作时,服务器会发布相应的事件,例如博主登录管理员后台时发布 "日志记录" 事件,用户浏览文章时发布 "访问文章" ...

  6. Spring Boot 学习笔记1——初体验之3分钟启动你的Web应用[z]

    前言 早在去年就简单的使用了一下Spring Boot,当时就被其便捷的功能所震惊.但是那是也没有深入的研究,随着其在业界被应用的越来越广泛,因此决定好好地深入学习一下,将自己的学习心得在此记录,本文 ...

  7. Spring Boot 学习笔记1---初体验之3分钟启动你的Web应用

    前言 早在去年就简单的使用了一下Spring Boot,当时就被其便捷的功能所震惊.但是那是也没有深入的研究,随着其在业界被应用的越来越广泛,因此决定好好地深入学习一下,将自己的学习心得在此记录,本文 ...

  8. Spring Boot 学习笔记--整合Thymeleaf

    1.新建Spring Boot项目 添加spring-boot-starter-thymeleaf依赖 <dependency> <groupId>org.springfram ...

  9. Spring Boot 学习笔记

    参考资料 http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ Spring Boot简介 Spring Boot使 ...

随机推荐

  1. JMeter学习(十六)JMeter测试Java(二)(转载)

    转载自 http://www.cnblogs.com/yangxia-test 实例: 服务为:将输入的两个参数通过IO存入文件: 1.打开MyEclipse,编写Java代码 服务: package ...

  2. 五、Singleton 单例模式

    需求:保证对象只创建一次 说明: 分为懒汉式.饿汉式,通过是否一开始就创建静态对象.饿汉式需要考虑线程并发的安全 懒汉式: public class Singleton { private stati ...

  3. springboot 缓存

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

  4. python数据分析之matplotlib学习

    本文作为学习过程中对matplotlib一些常用知识点的整理,方便查找. 类MATLAB API 最简单的入门是从类 MATLAB API 开始,它被设计成兼容 MATLAB 绘图函数. from p ...

  5. PAT1021(dfs 连通分量)

    A graph which is connected and acyclic can be considered a tree. The height of the tree depends on t ...

  6. FZU2150 :Fire Game (双起点BFS)

    传送门:点我 题意:“#”是草,"."是墙,询问能不能点燃俩地方,即点燃俩“#”,把所有的草烧完,如果可以,那么输出最小需要的时间,如果不行输出-1 思路:暴力BFS,看到n和m都 ...

  7. PHPlaravel中从数据库中选择数据是增加时间条件及各种条件

    注:附加条件后要加get函数. 1.public function getForDataTable($startTime,$endTime){ return $this->query() -&g ...

  8. 《学习OpenCV(中文版)》

    <模式识别中文版(希)西奥多里蒂斯> <学习OpenCV(中文版)> 矩阵计算 英文版 第四版 Matrix Computations OpenCV 3.x with Pyth ...

  9. jquery一些容易混淆的

    1.父级元素.append子集元素 == 子集元素.appendTo父级元素 2.父级元素.prepend子集元素 == 子集元素.prependTo父级元素 3.同辈1.insertBefore同辈 ...

  10. 转:WEB前端性能优化规则

    14条规则摘自<High Performance Web Sites>,本文地址 1.减少Http请求 使用图片地图 使用CSS Sprites 合并JS和CSS文件 这个是由于浏览器对同 ...