springboot 事件监听器
前言
在Spring框架中提供了一系列具有高度灵活性和可扩展性的功能模块。开发人员能够通过这些功能模块实现复杂的业务逻辑设计。这些功能模块不仅支持快速部署还能有效促进不同业务组件之间的解耦与独立。
引导案例
下面看一个简单的案例,
@Configuration
public class SelfBusiness {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SelfBusiness.class);
context.getBean(MyService.class).doBusiness();
context.close();
}
@Component
static class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
@Autowired
private ApplicationEventPublisher publisher;
public void doBusiness (){
logger.debug("主线业务");
logger.debug("发送短信");
logger.debug("发送邮件");
}
}
AI助手
运行上面的代码,观察效果

结合输出结果,在完成主线业务后需完成的功能包括发送短信、邮件及其他相关操作。这种做法没有问题但显得较为笨拙就业务扩展性而言不够友好如果后续还需在主线业务结束后新增其他审计操作则会迫使我们不得不引入额外的代码逻辑这将导致主线业务与其附属的支线操作深度绑定;
意思是说,我们预期的目标是确保主线业务不会受到其他业务操作的影响,并且仅需处理自身逻辑流程即可;这要求我们依赖于Spring框架提供的事件监听机制来实现相关的功能。
使用事件监听器改造过程
本系统框架基于Spring Boot(Spring)实现了事件监听器的主要应用模式,在实际开发中主要采用以下两种实现方式:一种是通过接口实现 listener 的具体功能形式;另一种则是在类体上直接添加注解形式完成事件监听功能;下面将详细阐述这两种实现方法及其特点。
一、通过实现ApplicationListener接口实现步骤
1、自定义一个事件类(对象),继承ApplicationEvent
static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
AI助手
在代码实现中,在各个模块之间可能存在多类数据传输事件;每个模块都与特定的数据传输类型相关联;对于一个特定的数据流处理组件来说,在接收数据时它只会关注于一种确定的数据类型;
2、自定义业务类实现ApplicationListener 接口
@Data
static class Params {
private String id ;
private String name;
private String phone;
}
@Component
static class SmsApplicationListener implements ApplicationListener<MyEvent> {
private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object source = myEvent.getSource();
try {
Params params = objectMapper.readValue(source.toString(), Params.class);
logger.debug("userId : {}",params.getId());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
logger.debug("执行 sms 发短信业务");
}
}
@Component
static class EmailApplicationListener implements ApplicationListener<MyEvent> {
private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object source = myEvent.getSource();
logger.debug("执行 email 发邮件业务");
}
}
AI助手
在实际应用开发中,在这一阶段的过程当中,在我们需要进行监控的事件类型即是我们之前所定义的MyEvent。一旦业务流程启动时就会自动触发相应的事件响应机制,在onApplicationEvent阶段即可获取相关的参数信息,并据此执行相应的发短信(发邮件)功能模块。
3、主线业务发布事件
@Component
static class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
@Autowired
private ApplicationEventPublisher publisher;
public void doBusiness (){
Params params = new Params();
params.setId("001");
params.setName("xiaoma");
params.setPhone("133******");
logger.debug("主线业务");
try {
publisher.publishEvent(new MyEvent(objectMapper.writeValueAsString(params)));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//publisher.publishEvent(new MyEvent("MyService doBusiness()"));
//logger.debug("发送短信");
//logger.debug("发送邮件");
}
}
AI助手
对于主线业务来说,在这个阶段不需要单独开发消息推送功能模块(如发送短信或邮件),只需通过一个publisher触发事件即可。如果需要传递参数,则在触发时一并提供。
完整的代码
@Configuration
public class SelfBusiness {
private static ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SelfBusiness.class);
context.getBean(MyService.class).doBusiness();
context.close();
}
@Data
static class Params {
private String id ;
private String name;
private String phone;
}
/** * 自定义事件对象
*/
static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
@Component
static class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
@Autowired
private ApplicationEventPublisher publisher;
public void doBusiness (){
Params params = new Params();
params.setId("001");
params.setName("xiaoma");
params.setPhone("133******");
logger.debug("主线业务");
try {
publisher.publishEvent(new MyEvent(objectMapper.writeValueAsString(params)));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//publisher.publishEvent(new MyEvent("MyService doBusiness()"));
//logger.debug("发送短信");
//logger.debug("发送邮件");
}
}
/** * 监听事件触发后要执行的业务
*/
@Component
static class SmsApplicationListener implements ApplicationListener<MyEvent> {
private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object source = myEvent.getSource();
try {
Params params = objectMapper.readValue(source.toString(), Params.class);
logger.debug("userId : {}",params.getId());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
logger.debug("执行 sms 发短信业务");
}
}
@Component
static class EmailApplicationListener implements ApplicationListener<MyEvent> {
private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object source = myEvent.getSource();
logger.debug("执行 email 发邮件业务");
}
}
}
AI助手
再次运行上面的代码,观察效果,可以看到,仍然能满足预期的效果

二、通过添加 @EventListener 注解来实现
这种方式不需要实现ApplicationListener API,
而是可以在监听类的方法中添加@addEventListener注释,
相对于其他方法而言,
这种方案较为简化。
下面具体展示完整的代码如下:
package com.congge.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Configuration
public class SelfBusiness2 {
private static ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SelfBusiness2.class);
context.getBean(MyService.class).doBusiness();
context.close();
}
@Data
static class Params {
private String id ;
private String name;
private String phone;
}
/** * 自定义事件对象
*/
static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
@Component
static class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
@Autowired
private ApplicationEventPublisher publisher;
public void doBusiness (){
Params params = new Params();
params.setId("001");
params.setName("xiaoma");
params.setPhone("133******");
logger.debug("主线业务");
try {
publisher.publishEvent(new MyEvent(objectMapper.writeValueAsString(params)));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
@Component
static class SmsListenerService {
private static final Logger logger = LoggerFactory.getLogger(SmsListenerService.class);
@EventListener
public void smsListener(MyEvent myEvent){
Object source = myEvent.getSource();
try {
SelfBusiness2.Params params = objectMapper.readValue(source.toString(), SelfBusiness2.Params.class);
logger.debug("userId : {}",params.getId());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
logger.debug("执行 sms 发短信业务");
}
}
@Component
static class EmailListenerService {
private static final Logger logger = LoggerFactory.getLogger(EmailListenerService.class);
@EventListener
public void emailListener(MyEvent myEvent){
Object source = myEvent.getSource();
try {
SelfBusiness2.Params params = objectMapper.readValue(source.toString(), SelfBusiness2.Params.class);
logger.debug("userId : {}",params.getId());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
logger.debug("执行 email 发邮件业务");
}
}
}
AI助手
运行上面的代码,观察效果,同样可以达到预期的效果

三、使用异步
为了深入探讨如何优化主线业务流程的效率时
通过查看源码可以得知,默认情况下会使用单线程同步机制进行事件发布。若希望实现异步处理,则需自定义一个ThreadPoolTaskExecutor实例,并注册一个SimpleApplicationEventMulticaster实例。因此我们只需为这两个核心组件创建相应的Bean即可实现异步功能。在业务处理类中增加这两个核心Bean以实现异步功能。
@Bean
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
return executor;
}
@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor executor) {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(executor);
return eventMulticaster;
}
AI助手
这时候再次运行代码,反复运行多次,就可以看到效果

对比下上面单线程效果

