spring event
昨天看到了一遍关于spring event的帖子,觉得很好,就照着敲了一份代码,感觉对spring event有了进一步的认识。帖子链接:https://segmentfault.com/a/1190000011433514。
spring event 封装了底层,为我们方便提供了事件的发布、消息订阅。其底层原理是观察者模式。
下面直接上代码:
定义一个bean如下,使用了lombok插件(真的挺好用):
package com.abc.model; import lombok.Data; @Data
public class User {
private String name;
private String password;
}
定义一个一个注册事件:
package com.abc.event; import com.abc.model.Dog;
import com.abc.model.User;
import lombok.Getter;
import org.springframework.context.ApplicationEvent; @Getter
public class UserRegisterEvent extends ApplicationEvent { private User user; public UserRegisterEvent(Object source, User user) {
super(source);
this.user = user;
}
}
编写service:
这里说明一下,applicationContext.publishEvent(new UserRegisterEvent(this, user)) 这个是发布事件的方法。到这里就完成了事件的发布了。当controller里面请求时候,service响应发布事件。
package com.tuandai.service; import com.tuandai.event.UserRegisterEvent;
import com.tuandai.model.Dog;
import com.tuandai.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service; @Service
public class UserRegisterService {
@Autowired
private ApplicationContext applicationContext; //发布事件
public void register(User user) {
//做些其他业务
applicationContext.publishEvent(new UserRegisterEvent(this, user));
}
}
定义监听器,有两种方式。
第一种,使用@EventListener 注解方式:
package com.tuandai.listener; import com.tuandai.event.UserRegisterEvent;
import com.tuandai.model.User;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component; @Component
public class AnnotationListener {
@EventListener
//这里如果加上@EventListener,代码运行的时候就会自动扫描到这里,也就是会执行监听的动作
public void register(UserRegisterEvent userRegisterEvent) {
User user = userRegisterEvent.getUser();
System.out.println("@UserRegisterEvent注册信息,狗命:" + user.getName() + ",密码:" + user.getPassword());
}
}
第二种,继承@ApplicationListener接口的方式,重写onApplicationEvent方法:
package com.tuandai.listener; import com.tuandai.event.UserRegisterEvent;
import com.tuandai.model.User;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component; //@Component 这里如果加上@Component,代码运行的时候就会自动扫描到这里,也就是会执行监听的动作
public class RegisterListener implements ApplicationListener<UserRegisterEvent> {
@Override
public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
//做些其他业务
User user=userRegisterEvent.getUser(); System.out.println("注册信息,用户名:"+user.getName()+",密码:"+user.getPassword());
}
}
package com.abc.listener;
import com.abc.event.UserRegisterEvent;
import com.abc.model.User;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
//这里如果加上@Component,代码运行的时候就会自动扫描到这里,也就是会执行监听的动作
public class RegisterListener implements ApplicationListener<UserRegisterEvent> {
@Override
public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
//做些其他业务
User user=userRegisterEvent.getUser();
System.out.println("注册信息,用户名:"+user.getName()+",密码:"+user.getPassword());
}
}
请求controller:http://localhost:9001/register?name=admin&password=123
结果如下:

可以看到,的确是做到了观察者的模式,订阅了消息。
下面再定义一个监听器,用于在注册之后发送邮件,这个也是基于@EventListener注解的方式的,代码如下:
package com.tuandai.listener; import com.tuandai.event.UserRegisterEvent;
import com.tuandai.model.User;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component; @Component
public class EmalRegisterListener {
@EventListener
// 这里如果加上@EventListener,代码运行的时候就会自动扫描到这里,也就是会执行监听的动作
public void sendEmal(UserRegisterEvent userRegisterEvent) {
User user = userRegisterEvent.getUser();
System.out.println("发送邮件,用户名是:" + user.getName() + ",密码是:" + user.getPassword());
}
}
再次运行,结果:

在这里可以看到,是发送邮件的监听器先于注册监听器监听到注册信息,这个在实际的生产环境中,应该是先注册这块业务,然后再是发送邮件。为此,spring event提供了一个监听先后顺序的机制:SmartApplicationListener,可保证实际的监听效果符合我们的期望。
定义一个有序的监听注册的监听器:
这段代码中,重写的 supportsEventType方法,用于确保监听的事件是用户注册的事件,实际生产环境中,可以换成自己的定义的事件。
package com.abc.listener; import com.abc.event.UserRegisterEvent;
import com.abc.model.User;
import com.abc.service.UserRegisterService;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; @Component
public class SmartRegisterListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
return aClass== UserRegisterEvent.class;
} @Override
public boolean supportsSourceType(Class<?> aClass) {
return aClass== UserRegisterService.class;
} /**
* 只有supportsEventType和supportsSourceType返回true的时候,才执行这个方法
* @param applicationEvent
*/
@Override public void onApplicationEvent(ApplicationEvent applicationEvent) {
UserRegisterEvent userRegisterEvent=(UserRegisterEvent) applicationEvent;
User user=userRegisterEvent.getUser();
System.out.println("注册用户,用户名是:"+ user.getName());
} @Override
public int getOrder() {
return 0; //这里数字越小,监听的优先级越高
}
}
定义一个智能的发送邮件的监听器:
package com.tuandai.listener; import com.tuandai.event.UserRegisterEvent;
import com.tuandai.model.User;
import com.tuandai.service.UserRegisterService;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component; /**
* 智能发送邮件监听器
*/
@Component
public class SmartEmailRegisterListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
return aClass== UserRegisterEvent.class;
} @Override
public boolean supportsSourceType(Class<?> aClass) {
return aClass== UserRegisterService.class;
} @Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
UserRegisterEvent userRegisterEvent=(UserRegisterEvent) applicationEvent;
User user=userRegisterEvent.getUser();
System.out.println("发送邮件,用户名是:"+ user.getName());
} @Override
public int getOrder() {
return 1; //可以是负数
}
}
在这两段代码中,均有个getOrder()方法,用于定义该监听器的优先级。这个数字越小,优先级越高。
再次执行,执行结果如下:

可以看到已经是按照优先级,先注册用户,再发邮件了。
以上的监听事件,都是同步的方式。实际生产环境你中,由于某些事件可能需要耗时较大,所有事件都用同步的方式,会降低体验效果。为此,spring也提供了异步的监听事件方式。
异步监听的关键字:@Async 。但是单单使用这个关键字,还不能起作用,还需要有个自定义的配置,用于继承AsyncConfigurer。
代码如下:
package com.tuandai.confiuration; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component; import java.util.concurrent.Executor; @Configuration
@EnableAsync //需要加上这个注解。同事需要继承AsyncConfigurer这个才能实现异步
public class ListenerAsyncConfiguration implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor=new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(10);
threadPoolTaskExecutor.setQueueCapacity(25);
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
} @Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
修改注册监听器:
package com.abc.listener;
import com.abc.event.UserRegisterEvent;
import com.abc.model.User;
import com.abc.service.UserRegisterService;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class SmartRegisterListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
return aClass== UserRegisterEvent.class;
}
@Override
public boolean supportsSourceType(Class<?> aClass) {
return aClass== UserRegisterService.class;
}
/**
* 只有supportsEventType和supportsSourceType返回true的时候,才执行这个方法
* @param applicationEvent
*/
@Override
@Async
public void onApplicationEvent(ApplicationEvent applicationEvent) {
try {
Thread.sleep(1000*10);
} catch (InterruptedException e) {
e.printStackTrace();
}
UserRegisterEvent userRegisterEvent=(UserRegisterEvent) applicationEvent;
User user=userRegisterEvent.getUser();
System.out.println("注册用户,用户名是:"+ user.getName());
}
@Override
public int getOrder() {
return 0; //这里数字越小,监听的优先级越高
}
}
在这段代码中,onApplicationEvent方法添加了@Async注解,声明这个方法是异步的方法。这段代码里,会沉睡10秒的时间。
再次运行的时候,运行的速度很快,根本不用1s,证明这个监听器的异步的方式确是起作用了。
执行结果如下:

这里要注意到的是,发送邮件的监听器的执行,先于注册监听器了。之所以出现这种情况,是因为监听器在使用异步的方式的时候,getOrder()这个方法不再起作用了。
因此,个人认为,实际生产环境中,异步的监听器应该是做一些不太重要,优先级不高,不需要实时的业务,比如主站注册,然后把数据同步到其他的副站。
而同步的方式,则是用于做一些实时性较强,并且对顺序有着严格要求的业务。比如,先在主站注册,后发发送邮件。
spring event的更多相关文章
- EventBus VS Spring Event
EventBus VS Spring Event 本地异步处理,采用事件机制 可以使 代码解耦,更易读.事件机制实现模式是 观察者模式(或发布订阅模式),主要分为三部分:发布者.监听者.事件. Gua ...
- Spring Event事件驱动
Spring事件驱动模型,简单来说类似于Message-Queue消息队列中的Pub/Sub发布/订阅模式,也类似于Java设计模式中的观察者模式. 自定义事件 Spring的事件接口位于org.sp ...
- 自定义Spring event
通过Spring自定义event 首先我们定义我们的event类 package com.hyenas.spring.custom.event; import org.springframework. ...
- Event Handling in Spring
Spring内置的event有 1.ContextRefreshedEvent This event is published when the ApplicationContext is eithe ...
- spring中自定义Event事件的使用和浅析
在我目前接触的项目中,用到了许多spring相关的技术,框架层面的spring.spring mvc就不说了,细节上的功能也用了不少,如schedule定时任务.Filter过滤器. intercep ...
- Spring学习六:自定义Event事件
Spring 中的自定义事件 编写和发布自己的自定义事件有许多步骤.按照在这一章给出的说明来编写,发布和处理自定义 Spring 事件. 步骤 描述 1 创建一个名称为 SpringExample 的 ...
- 如何使用spring中的Log4jConfigListener--删除
使用spring中的Log4jConfigListener有如如下好处: 1. 动态的改变记录级别和策略,不需要重启Web应用,如<Effective Enterprise Java> ...
- spring事件驱动模型--观察者模式在spring中的应用
spring中的事件驱动模型也叫作发布订阅模式,是观察者模式的一个典型的应用,关于观察者模式在之前的博文中总结过,http://www.cnblogs.com/fingerboy/p/5468994. ...
- Spring事件解析
首先介绍Spring事件相关类的关系: 其中EventListener与EventObject均是Java SE的范畴,源码如下: package java.util; public interfac ...
随机推荐
- 【C#基础知识】C#控制台程序入口函数 Main(string[] args) 参数详解
测试环境vs2019+.net5.0 请看 :https://cloud.tencent.com/developer/article/1507934 本测试环境vs2022+.net6.0 +wind ...
- Winform实现客户端的自动更新
话不多说,直接上干货.当然也希望各位前辈多多指导. 自动更新客户端的设计原理图 请花两分钟时间,仔细阅读下面这张图,明白客户端自动升级的原理. 自动更新的效果图 1. ...
- 小程序根据ID跳转到不同的分页
想实现效果: 点击后跳转 wxml: <view class="fiveson"> <view class="fiveson-son&q ...
- Pycharm:调试、断点
1.调试:Shift+F10 或 2.快捷键: 步进:F8 进入函数内:F7 运行到下一个断点处:F9 3.删除所有断点 菜单栏->Run->View BreakPoints.. 减号代表 ...
- JZ-065-矩阵中的路径
矩阵中的路径 题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路 ...
- 微信小程序结合原生JS实现电商模板(二)
接 <微信小程序结合原生JS实现电商模板(一)>,在首页列表加入购物车到购物和模块增删数量,动态计算商品价格实现后,本次提交主要实现了商品详情(还不完善)简单页面,从商品详情页跳转到购物车 ...
- Mysql-关系型数据库与非关系型数据库
一.什么是数据库 数据库是数据的仓库. 与普通的"数据仓库"不同的是,数据库依据"数据结构"来组织数据,因为"数据结构",所以我们看到的数据 ...
- Export大数据量导出和打包
项目需求 导出生成大批量数据的文件,一个Excel中最多存有五十万条数据,查询多余五十万的数据写多个Excel中.导出完成是生成的多个Excel文件打包压缩成zip,而后更新导出记录中的压缩文件路 ...
- AE初步
AE开发就是我们常说的ArcEngine二次开发. 1.配置环境 目前来讲,稳定的版本配置,一般配置为VS2012+ArcEngine10.2. 安装ArcEngine时,安装图中三项即可,顺序为Ar ...
- Redis pub/sub
list 类型, lpush + rpop 或 lpush + brpop 用作消息队列时,消息只能消费一次,且不支持多个消费者(消息只能消费一次),并且在客户端崩溃时容易丢失消息.而 pub/sub ...