当前位置: 首页 > news >正文

Springboot之监听器

Springboot之事件监听器

  • 事件监听的几种方式
    • 1 方式一:实现接口
      • 1.1 创建事件
      • 1.2 创建事件监听器
      • 1.3 发布事件
    • 2 方式二:注解方式
      • 2.1 创建事件
        • 2.1.1 创建发送邮件事件
        • 2.1.2 创建发送短信事件
      • 2.2 创建事件监听器
      • 2.3 发布事件
      • 2.4 事件异步处理(方式二有效)
        • 2.4.1 配置线程池
        • 2.4.2 设置事件执行的线程池
    • 3 项目使用案例
      • 3.1 创建事件父类
      • 3.2 创建监听器接口
      • 3.3 创建事件监听器管理类
      • 3.4 创建事件监听器的简单实现
      • 3.5 创建用户注册事件
      • 3.6 创建用户注册服务
      • 3.7 创建事件监听配置类
      • 3.8 创建发送邮件服务
      • 3.9 创建发送短信服务
      • 3.10 调用用户注册服务
      • 3.11 调用赠送优惠券服务(扩展)
    • 4 事件异步处理
      • 4.1 启动类开启异步
      • 4.2 创建线程池
      • 4.3 事件方法开启异步
      • 4.4 修改事件类型的获取方式
    • 5 拓展
      • 5.1 控制发送邮件、短信、优惠券服务的执行顺序

事件监听的几种方式

1 方式一:实现接口

场景:用户注册成功后,给用户赠送100元优惠券

1.1 创建事件

实现ApplicationEvent接口

package com.per.listener.e1;import com.per.domain.UserDto;
import org.springframework.context.ApplicationEvent;/*** @Title DemoEvent1* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
public class UserEvent extends ApplicationEvent {/*** 事件源*/private UserDto userDto;public UserEvent(UserDto userDto) {super(userDto);this.userDto = userDto;}/*** 获取事件中的用户信息** @return*/public UserDto getUserDto() {return userDto;}
}

1.2 创建事件监听器

实现ApplicationListener接口,重写onApplicationEvent方法

package com.per.listener.e1;import com.per.domain.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;/*** @Title UserListener* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Slf4j
@Component
public class UserListener implements ApplicationListener<UserEvent> {@Overridepublic void onApplicationEvent(UserEvent event) {log.info("UserListener#onApplicationEvent 事件监听 开始执行...");UserDto userDto = event.getUserDto();// 给用户发优惠券log.info("给用户{}发送100元优惠卷", userDto.getName());}
}

1.3 发布事件

引入ApplicationContext,调用publishEvent方法发布事件

package com.per.service.impl;import com.per.domain.UserDto;
import com.per.listener.e1.UserEvent;
import com.per.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;/*** @Title UserServiceImpl* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {@Autowiredprivate ApplicationContext applicationContext;@Overridepublic String userRegister(UserDto userDto) {log.info("用户{}注册成功,注册信息: {}", userDto.getName(), userDto.toString());// 发送优惠券UserEvent userEvent = new UserEvent(userDto);applicationContext.publishEvent(userEvent);return String.format("用户%s注册成功,并赠送了100元优惠券", userDto.getName());}
}

2 方式二:注解方式

场景:用户注册成功后,给用户发送邮件、短信通知

2.1 创建事件

继承ApplicationEvent,重写事件构造方法

2.1.1 创建发送邮件事件
package com.per.listener.e2;import org.springframework.context.ApplicationEvent;/*** @Title SendMailEvent 发送邮件事件* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
public class SendMailEvent extends ApplicationEvent {public SendMailEvent(Object source) {super(source);}}
2.1.2 创建发送短信事件
package com.per.listener.e2;import org.springframework.context.ApplicationEvent;/*** @Title SendShortMsg 发送短信事件* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
public class SendMsgEvent extends ApplicationEvent {public SendMsgEvent(Object source) {super(source);}
}

2.2 创建事件监听器

定义事件执行的方法,使用注解@EventListener标注方法,使用classes属性指定方法对应的事件

package com.per.listener.e2;import com.per.domain.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @Title UserE2Listener* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Component
@Slf4j
public class UserE2Listener {/*** 发送邮件* @param sendMailEvent 发送邮件事件*/@EventListener(classes = SendMailEvent.class)@Order(1)public void sendMail(SendMailEvent sendMailEvent){UserDto userDto = (UserDto) sendMailEvent.getSource();log.info("发送邮件给用户{} ...【邮件】恭喜你注册成功", userDto.getName());}/*** 发送短信* @param sendMsgEvent 发送短信事件*/@EventListener(classes = SendMsgEvent.class)@Order(2)public void sendMsg(SendMsgEvent sendMsgEvent){UserDto userDto = (UserDto) sendMsgEvent.getSource();log.info("发送短信给用户{} ...【短信】恭喜你注册成功", userDto.getName());}}

2.3 发布事件

引入ApplicationEventPublisher,调用publishEvent方法发布事件

package com.per.service.impl;import com.per.domain.UserDto;
import com.per.listener.e2.SendMailEvent;
import com.per.listener.e2.SendMsgEvent;
import com.per.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;/*** @Title UserServiceImpl* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {@Autowiredprivate ApplicationEventPublisher publisher;@Overridepublic String userRegister1(UserDto userDto) {log.info("用户{}注册成功,注册信息: {}", userDto.getName(), userDto.toString());// 发送邮件SendMailEvent sendMailEvent = new SendMailEvent(userDto);publisher.publishEvent(sendMailEvent);// 发送短息SendMsgEvent sendMsgEvent = new SendMsgEvent(userDto);publisher.publishEvent(sendMsgEvent);return String.format("用户%s注册成功,并发送邮件和短信通知", userDto.getName());}}

2.4 事件异步处理(方式二有效)

2.4.1 配置线程池
package com.per.listener.e3.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;/*** @Title ThreadPoolConfig* @Description TODO* @Author Lee* @Date 2024-01-20*/
@Configuration
public class ThreadPoolConfig {@Bean("eventListenerThreadPool")public Executor taskExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();taskExecutor.setCorePoolSize(5);taskExecutor.setMaxPoolSize(10);taskExecutor.setKeepAliveSeconds(60);taskExecutor.setQueueCapacity(50);taskExecutor.setThreadNamePrefix("myExecutor--");taskExecutor.setWaitForTasksToCompleteOnShutdown(true);taskExecutor.setAwaitTerminationSeconds(60);taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());taskExecutor.initialize();return taskExecutor;}}
2.4.2 设置事件执行的线程池
package com.per.listener.e3.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import javax.annotation.Resource;/*** @Title EventListenerAsynConfig* @Description TODO* @Author Lee* @Date 2024-01-20*/
@Configurationpublic class EventListenerAsyncConfig {@Resourceprivate ThreadPoolTaskExecutor eventListenerThreadPool;@Beanpublic ApplicationEventMulticaster applicationEventMulticaster() {SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();eventMulticaster.setTaskExecutor(eventListenerThreadPool);return eventMulticaster;}
}

3 项目使用案例

场景:用户注册成功,给用户发送邮件、短信通知

3.1 创建事件父类

package com.per.listener.e3.event;/*** @Title AbstractEvent 所有事件父类* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
public abstract class AbstractEvent {/*** 事件源*/protected Object source;public AbstractEvent(Object source) {this.source = source;}/*** 获取事件源** @return*/public Object getSource() {return source;}/*** 设置事件源** @param source*/public void setSource(Object source) {this.source = source;}
}

3.2 创建监听器接口

package com.per.listener.e3.listener;import com.per.listener.e3.event.AbstractEvent;/*** @Title EventListener 事件监听器接口* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
public interface EventListener<E extends AbstractEvent> {/*** 处理事件** @param event 要处理的事件*/void onEvent(E event);
}

3.3 创建事件监听器管理类

package com.per.listener.e3.listener;import com.per.listener.e3.event.AbstractEvent;/*** @Title EventListenerManager 事件监听器管理类* @ProjectName spring-boot-demo* @Description 1.负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来)*              2.负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件)* @Author Lee* @Date 2024-01-17*/
public interface EventListenerManager {/*** 广播事件给所有监听器** @param event 事件*/void pushEvent(AbstractEvent event);/*** 添加一个事件监听器** @param listener 事件监听器*/void addListener(EventListener<?> listener);/*** 删除一个事件监听器** @param listener 事件监听器*/void removeListener(EventListener<?> listener);
}

3.4 创建事件监听器的简单实现

package com.per.listener.e3.listener.impl;import com.per.listener.e3.event.AbstractEvent;
import com.per.listener.e3.listener.EventListener;
import com.per.listener.e3.listener.EventListenerManager;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @Title SimpleEventListener 事件广播器的简单实现* @Description TODO* @Author Lee* @Date 2024-01-17*/
public class SimpleEventListener implements EventListenerManager {private Map<Class<?>, List<EventListener>> eventListenerMap = new ConcurrentHashMap<>();@Overridepublic void pushEvent(AbstractEvent event) {List<EventListener> eventListeners = this.eventListenerMap.get(event.getClass());if (eventListeners != null) {for (EventListener eventListener : eventListeners) {// 执行事件eventListener.onEvent(event);}}}@Overridepublic void addListener(EventListener<?> listener) {Class<?> eventType = this.getEventType(listener);// 查询map中事件对应的监听器List<EventListener> eventListeners = this.eventListenerMap.get(eventType);if (eventListeners == null) {// 事件对应的监听器集合为空,保存事件和监听器到map中,key:事件 value:空的监听器集合eventListeners = new ArrayList<>();this.eventListenerMap.put(eventType, eventListeners);}// 事件对应的监听器集合不为空,添加监听器到事件对应的监听器集合中eventListeners.add(listener);}@Overridepublic void removeListener(EventListener<?> listener) {Class<?> eventType = this.getEventType(listener);// 查询map中事件对应的监听器List<EventListener> eventListeners = this.eventListenerMap.get(eventType);if (eventListeners != null) {// 事件对应的监听器集合不为空,从事件对应的监听器集中删除当前监听eventListeners.remove(listener);}}protected Class<?> getEventType(EventListener eventListener) {// 获取直接实现eventListener接口的类或接口的Type// 异步@Async时使用这种方式获取
//        ParameterizedType parameterizedType = (ParameterizedType)eventListener.getClass().getSuperclass().getGenericInterfaces()[0];// 同步时使用这种方式获取ParameterizedType parameterizedType = (ParameterizedType) eventListener.getClass().getGenericInterfaces()[0];// 获取EventListener中泛型的实际类型Type eventType = parameterizedType.getActualTypeArguments()[0];return (Class<?>) eventType;}
}

3.5 创建用户注册事件

package com.per.listener.e3.event;import com.per.domain.UserDto;/*** @Title RegisterSuccessEvent 用户注册成功事件* @Description TODO* @Author Lee* @Date 2024-01-17*/
public class RegisterSuccessEvent extends AbstractEvent {/*** 用户信息*/private UserDto userDto;/*** 用户注册成功事件** @param source  事件源* @param userDto 用户信息*/public RegisterSuccessEvent(Object source, UserDto userDto) {super(source);this.userDto = userDto;}public UserDto getUserDto() {return userDto;}public void setUserDto(UserDto userDto) {this.userDto = userDto;}
}

3.6 创建用户注册服务

package com.per.service.impl;import com.per.domain.UserDto;
import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListenerManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;/*** @Title UserRegisterService 用户注册成功服务* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Slf4j
public class UserRegisterService {/*** 事件发布者*/@Autowiredprivate EventListenerManager eventListenerManager;/*** 用户注册** @param userDto 用户信息*/public void registerUser(UserDto userDto) {log.info("用户{}注册成功", userDto.getName());// 执行其他监听事件this.eventListenerManager.pushEvent(new RegisterSuccessEvent(this, userDto));}public EventListenerManager getEventListenerManager() {return eventListenerManager;}public void setEventListenerManager(EventListenerManager eventListenerManager) {this.eventListenerManager = eventListenerManager;}
}

3.7 创建事件监听配置类

package com.per.listener.e3.config;import com.per.listener.e3.listener.EventListener;
import com.per.listener.e3.listener.EventListenerManager;
import com.per.listener.e3.listener.impl.SimpleEventListener;
import com.per.service.impl.UserRegisterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;import java.util.List;/*** @Title EventListenerConfig* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Configuration
@Component
public class EventListenerConfig {/*** 注册一个事件发布者** @param eventListeners 事件* @return*/@Bean@Autowired(required = false) // 当eventListeners不存在时不抛出异常public EventListenerManager eventListenerManager(List<EventListener> eventListeners) {EventListenerManager eventListenerManager = new SimpleEventListener();if (eventListeners != null) {eventListeners.forEach(eventListener -> eventListenerManager.addListener(eventListener));}return eventListenerManager;}/*** 注册一个用户注册服务** @param eventListenerManager* @return*/@Beanpublic UserRegisterService userRegisterService(EventListenerManager eventListenerManager) {UserRegisterService userRegisterService = new UserRegisterService();userRegisterService.setEventListenerManager(eventListenerManager);return userRegisterService;}
}

3.8 创建发送邮件服务

package com.per.service.impl;import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @Title UserSendMailService 用户发送邮件服务* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Component
@Slf4j
public class UserSendMailService implements EventListener<RegisterSuccessEvent> {@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("给用户{}发送邮件,内容:恭喜你注册成功", event.getUserDto().getName());}
}

3.9 创建发送短信服务

package com.per.service.impl;import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @Title UserSendMsgService 用户发送短信服务* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Component
@Slf4j
public class UserSendMsgService implements EventListener<RegisterSuccessEvent> {@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("给用户{}发送短息,短信内容:恭喜你注册成功", event.getUserDto().getName());}
}

3.10 调用用户注册服务

    /*** 用户注册成功后发送邮件、发送短信** @return*/@RequestMapping(value = "register", method = RequestMethod.GET)public String register() {UserDto userDto = new UserDto(11, "李四", "男");userRegisterService.registerUser(userDto);return "SUCCESS";}

3.11 调用赠送优惠券服务(扩展)

新增场景:增加一个赠送优惠券业务

package com.per.service.impl;import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @Title SendUserCouponsService 发送优惠券服务* @Description TODO* @Author Lee* @Date 2024-01-20*/
@Component
@Slf4j
@Order(3)
public class SendUserCouponsService implements EventListener<RegisterSuccessEvent> {@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("给用户{}发送100元优惠券", event.getUserDto().getName());}
}

4 事件异步处理

注意:下面的修改皆基于3 项目使用案例修改

4.1 启动类开启异步

package com.per;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;@SpringBootApplication
@EnableAsync(proxyTargetClass = true)
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);}}

4.2 创建线程池

参考 2.4.1 配置线程池

4.3 事件方法开启异步

@Component
@Slf4j
@Order(1)
public class UserSendMailService implements EventListener<RegisterSuccessEvent> {@Async("eventListenerThreadPool")@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("{}-给用户{}发送邮件,内容:恭喜你注册成功", Thread.currentThread().getName(), event.getUserDto().getName());}
}@Component
@Slf4j
@Order(2)
public class UserSendMsgService implements EventListener<RegisterSuccessEvent> {@Async("eventListenerThreadPool")@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("{}--给用户{}发送短息,短信内容:恭喜你注册成功", Thread.currentThread().getName(), event.getUserDto().getName());}
}@Component
@Slf4j
@Order(3)
public class SendUserCouponsService implements EventListener<RegisterSuccessEvent> {@Async("eventListenerThreadPool")@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("{}--给用户{}发送100元优惠券", Thread.currentThread().getName(), event.getUserDto().getName());}
}

4.4 修改事件类型的获取方式

参考 3.4 创建事件监听器的简单实现

5 拓展

5.1 控制发送邮件、短信、优惠券服务的执行顺序

可以通过@Order控制服务的加载顺序实现

http://www.lryc.cn/news/286268.html

相关文章:

  • 【02】mapbox js api加载arcgis切片服务
  • Vue四个阶段,八个钩子函数
  • rancher和k8s接口地址,Kubernetes监控体系,cAdvisor和kube-state-metrics 与 metrics-server
  • idea编译打包前端vue项目
  • Unity中URP下的 额外灯 逐像素光 和 逐顶点光
  • 《WebKit 技术内幕》学习之五(2): HTML解释器和DOM 模型
  • Redis实战之-分布式锁-redission
  • 离线数据仓库-关于增量和全量
  • 09 STM32 - PWM
  • 三勾点餐系统java+springboot+vue3,开源系统小程序点餐系统
  • 《WebKit 技术内幕》学习之五(1): HTML解释器和DOM 模型
  • 小程序学习-21
  • Spring第七天(AOP)
  • 【0247】PG内核checkpoint实现机制分析(2)
  • 单例模式分享
  • Linux查找日志常用命令
  • 中国国际光伏展
  • openai assistants api接入微信机器人,实现类GPTs功能
  • 性能优化-OpenCL kernel 开发
  • systick定时器
  • Unity学习-逐帧图集动画制作
  • 鸿蒙使用第三方SO库
  • 宝塔FTP文件传输服务结合cpolar内网穿透实现远程连接本地服务
  • 【ARMv8M Cortex-M33 系列 7.4 -- 如何使能 usagefault | memmange fault | bus fault 中断】
  • Android Studio安卓开发--ListView学习整理
  • hyperf 和 laravel、lumen防止xss攻击中间件封装
  • flask web 学习之用户认证与会话管理
  • 更改wpf原始默认按钮的样式
  • 【协议】HTTP、HTTPS和HTTP2.0学习总结
  • [数据结构]顺序表