采用事件监听的好处

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

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

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

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

实际代码演示

首先是项目结构,如下

  • 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. 有用的url地址

    https://docs.oracle.com/javase/7/docs/api/overview-summary.html https://docs.oracle.com/javase/8/doc ...

  2. JMeter学习(三)元件的作用域与执行顺序(转载)

    转载自 http://www.cnblogs.com/yangxia-test 1.元件的作用域 JMeter中共有8类可被执行的元件(测试计划与线程组不属于元件),这些元件中,取样器是典型的不与其它 ...

  3. 【翻译】 View Frustum Culling --1 View Frustum’s Shape

    这是一些列来自lighthouse3d的视锥体裁剪教程.旨在学习总结,及便于查阅. 1.视锥体的形状 在OpenGL中,透视投影是由两个函数定义的gluPerspective和gluLookAt.我们 ...

  4. Python程序打包—pyinstaller

    简介:PyInstaller是一个十分有用的第三方库,通过对源文件打包,Python程序可以在没有安装 Python的环境中运行,也可以作为一个独立文件方便传递和管理. PyInstaller的官方网 ...

  5. Codeforces Round #450 (Div. 2)

    Codeforces Round #450 (Div. 2) http://codeforces.com/contest/900 A #include<bits/stdc++.h> usi ...

  6. sql查询 !='' 和 is not null的区别

    select * from table where a is not null 会把有内容的和内容为空的都查出来而select * from table where a != '' 只会把有内容的查出 ...

  7. [leetcode]445. Add Two Numbers II 两数相加II

    You are given two non-empty linked lists representing two non-negative integers. The most significan ...

  8. 用Python监听鼠标和键盘事件

    PyHook是一个基于Python的“钩子”库,主要用于监听当前电脑上鼠标和键盘的事件.这个库依赖于另一个Python库PyWin32,如同名字所显示的,PyWin32只能运行在Windows平台,所 ...

  9. 浅谈XListView的使用

    XListView是github上的开源第三方控件 内部封装好的一个类XListView 其内部包含XListViewFooter XListViewHeader 两个自定义类(控件)如此实现上拉加载 ...

  10. css背景图充满屏幕

    代码: body { /* 加载背景图 */ background: url(resource/inv_bg.png); /* 背景图不平铺 */background-repeat: no-repea ...