处于好奇我就百度了一下,琢磨了一下,参考了一个戏精博主的文章,写了个demo,这里记录一下
他是啥
首先这玩意呢,就是一个监听器
,他可以监听指定类型的事件
事件类继承ApplicationEvent
,在里面自定义荷载,由事件发布器ApplicationEventPublisher
调用方法.publishEvent(userActionEvent);
发布,一但发布马上就会被 @EventListener()
定义好的监听器监听到,从而执行方法内的内容
关键类
ApplicationEvent
,基础事件抽象类,继承这个类以后,类就成为了可发布事件,可以使用发布器对事件进行发布
ApplicationEventPublisher
,事件发布器,可以调用publishEvent
发布我们定义好的事件
@EventListener
,监听器配置注解,配置监听器参数,value=监听的具体事件,condition表达式监听的匹配规则,#参数…event的operate的name==“ADD“
AsyncConfigurer
,自定义线程池,事件监听有的业务需要避免阻塞线程,所以我们定义一个线程池。避免异步调用的时候频繁销毁创建线程
示例代码
User对象
任意一个POJO提供测试即可
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private String gender;
private Integer age;
public User(Long id, String name) {
this.id = id;
this.name = name;
}
}
EnumUserOperate
状态枚举,便于状态的维护
public enum EnumUserOperate {
ADD("add", 0, "新增"),
UPDATE("update", 1, "修改"),
DELETE("delete", 2, "删除");
private String name;
private Integer value;
private String desc;
/**
* 根据value获取枚举实例
* @param value
* @return
*/
public EnumUserOperate getByValue(Integer value) {
EnumUserOperate[] values = EnumUserOperate.values();
for (EnumUserOperate e : values) {
if (Objects.equals(e.getValue(), value))
return e;
}
return null;
}
关键
UserActionEvent事件对象
这个就是用来被监听的对象了,当事件发布器
发布这个事件的时候,监听这个事件的监听器
将被触发
/**
* 事件详情辅助对象,用以辅助事件的发布,发布的时候就发布这货,至于里面有什么,自己根据情况而定
* 届时那货也接Object,但是没意义,最好用这个定义好的,然后利用多态转型,以辅助事件的进行
*/
@EqualsAndHashCode(callSuper = true)
public class UserActionEvent extends ApplicationEvent {
// 操作是否成功
private Boolean success;
// 操作类型
private EnumUserOperate operate;
// 数据对象
private User user;
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public UserActionEvent(Object source) {
super(source);
}
}
@EventListener()事件监听注解
用以配置监听器,触发的时候将会执行监听器注解的那个方法
@Service
@Slf4j
public class UserMonitor {
/**
* 监听新增用户事件
* 异步操作使用自定义线程池
* EventListener注解中细化监听具体某种事件
* @param event 监听事件
* @return
*/
// 异步操作。指定线程池
@Async("lazyTraceExecutor")
// 配置监听器,value=监听的具体事件,condition监听的匹配规则,#参数....event的operate的name==“ADD“
@EventListener(value = UserActionEvent.class,condition = "#event.operate.name()=='ADD'")
public void addUserApplicationEvent(UserActionEvent event){
try {
User user = event.getUser();
log.info("监听到新增用户,:{}",user);
log.info("具体的操作,可以是新增以后的很多事情");
} catch (Exception e) {
log.error("事件:{},执行异常:{}",event,e.getMessage());
}
}
}
关于condition表达式
#下面方法的变量名.属性.属性 === xxx
监听器的另一种写法
/**
* 商品抽奖事件
*/
@Slf4j
@Service
public class GoodsSweepstakesListenerB implements ApplicationListener<GoodsSweepstakesEvent> {
public static final String nas="asd";
@Async(value = "threadPool")
@Override
public void onApplicationEvent(GoodsSweepstakesEvent event) {
log.info("B==>商品:{},已经售完开始抽奖",event.getGoods());
}
}
ApplicationEventPublisher事件发布器
在任意一个地方注入这个bean就可以调用方事件的发布
@Service
public class UserServiceImpl implements UserService {
// 注入事件发布器
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Override
public User addUser(User user) {
// 这里本该进行dao层操作,由于只是演示。直接使用入参对象返回。表示保存成功了
// 添加用户操作,发布通知:新增了user
User saveResult = saveUser(user);
UserActionEvent userActionEvent = new UserActionEvent(this);
userActionEvent.setOperate(EnumUserOperate.ADD);
userActionEvent.setSuccess(true);
userActionEvent.setUser(saveResult);
// 调用事件发布器,发布事件
applicationEventPublisher.publishEvent(userActionEvent);
log.info("发布了add事件:{}",userActionEvent);
return saveResult;
}
// 模拟一波DB操作
public User saveUser(User user){
return user;
}
}
AsyncConfigurer自定义线程池
配置一波自定义线程池
@Configuration
public class MyAsynConfigurer implements AsyncConfigurer {
/**
* 自定义线程池
*/
@Bean("lazyTraceExecutor")
public Executor getAsynExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 获取虚拟机可用的处理器最大数量
int core = Runtime.getRuntime().availableProcessors();
executor.setCorePoolSize(core); // 设置核心线程数
executor.setMaxPoolSize(core*2+1); // 设置最大线程数量
executor.setKeepAliveSeconds(3); // 除核心线程外,其他线程的最大存活时间
executor.setQueueCapacity(40); // 设置队列容量,如果传入值大于0,底层队列使用的是LinkedBlockingQueue,否则默认使用SynchronousQueue
executor.setThreadNamePrefix("jvm-executor-"); // 设置线程名称前缀
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 设置线程的拒绝策略
executor.initialize(); // 执行初始化
return executor;
}
}
在使用@Async("lazyTraceExecutor")进行异步操作的时候就可以通过指定这个异步方案bean的id名进行线程池的选用
注意需要在启动类上添加注解`@EnableAsync`启用异步操作
最终执行效果