Spring Event如何优雅实现系统业务解耦
一、介绍
Spring事件(Spring Event)
是Spring框架的一项功能,它允许不同组件之间通过发布-订阅机制进行解耦的通信。在Spring中,事件是表示应用程序中特定事件的对象,例如用户注册、订单创建、数据更新等。当这些事件发生时,可以通知其他组件来执行相应的操作。
具体来说,Spring事件机制包含以下几个主要的部分:
1、事件(Event):
事件是一个普通的POJO类,用于封装与应用程序状态变化相关的信息。通常情况下,事件类继承自ApplicationEvent抽象类,Spring中提供了一些内置的事件,也可以自定义事件。
2、事件发布者(ApplicationEventPublisher):
事件发布者是一个接口,用于发布事件。在Spring中,ApplicationContext就是一个事件发布者,可以通过ApplicationContext的publishEvent()方法来发布事件。
3、事件监听器(ApplicationListener):
事件监听器是一个接口,用于监听事件并在事件发生时执行相应的逻辑。在Spring中,我们可以通过实现ApplicationListener接口或使用@EventListener注解来定义事件监听器。
4、 事件监听器注册:
事件监听器需要注册到事件发布者(ApplicationContext)中,以便在事件发生时被正确调用。在Spring中,通常通过XML配置、注解或者编程方式将事件监听器注册到ApplicationContext中。
设计模式:订阅发布模式
Spring Event
是一种基于观察者模式(Observer Pattern)的实现。观察者模式(Observer Design
Pattern)也被称为发布订阅模式。其定义是:在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。
二、代码示例
Spring Boot并不会自动默认维护一个线程池来处理event事件,也就是说他的订阅发布是同步。要想异步处理事件使用 @Async标记即可,注意前提条件是:使用 @EnableAsync 开启 Spring 异步
2.1 定义实体类
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
private Long id;
private String userNo;
private String nickname;
private String email;
private String phone;
private Integer gender;
private Date birthday;
private Integer isDelete;
}
2.2 自定义一个注册事件
@Getter
public class RegisterEvent extends ApplicationEvent {
private Employee employee;
public RegisterEvent(Object source, Employee employee) {
super(source);
this.employee = employee;
}
}
注意这里的类名,对于多个发布事件,就是靠订阅不同的注册事件类名来区分
2.3 定义事件监听器
方法一
实现ApplicationListener接口
方法二
使用@EventListener注解。
@Slf4j
@Component // 把监听器注册到spring容器中,(这里的RegisterEvent就是指哪个发布事件)
@Async("asyncExecutor")
public class RegisterMsgNoticeListener implements ApplicationListener<RegisterEvent> {
@Override
public void onApplicationEvent(RegisterEvent event) {
log.info("=========>>>站内信通知了");
}
}
@Slf4j
@Component
public class RegisterPushDataListener{
//注意使用纾解的方式,@Async需加在方法上
@EventListener
@Async("asyncExecutor")
public void onApplicationEvent(RegisterEvent event) {
log.info("======>>>推送用户信息到大数据系统了,user={}", event.getUser());
}
}
2.4 发布事件
实现类
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Resource
private ApplicationContext applicationContext;
@Override
public void registerUser(User user) {
log.info("=====>>>user注册成功了");
applicationContext.publishEvent(new RegisterEvent(this, user));
}
}
注意:这里的ApplicationContext 是import org.springframework.context.ApplicationContext;
接口
public interface EmployeeService {
void register(Employee employee);
}
控制层
@PostMapping("/register")
public void register() {
Employee user = Employee.builder().userNo("1111").birthday(new Date()).gender(0)
.phone("120").email("123@163.com").nickname("雪飘人间").build();
employeeService.register(user);
}
2.5 主启动类加异步线程注解
@EnableAsync
@SpringBootApplication
public class SimpleSadApplication {
public static void main(String[] args) {
System.out.println("系统开始启动…………");
long l1 = System.currentTimeMillis();
SpringApplication.run(SimpleSadApplication.class, args);
long l2 = System.currentTimeMillis();
System.out.println("系统启动完毕,用时:"+(l2-l1)+"毫秒!");
}
}
2.6 自定义线程池
@Configuration
public class InitThreadPool {
@Bean(name = "asyncExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("asyncExecutor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
调用接口:可以看到,输出结果,订阅已发布也已经解耦了,但是多个订阅者的顺序是随机的
三、spring Event优缺点
优点:
简单易用: Spring Event是Spring框架提供的一个内置的事件发布-订阅机制,使用起来非常简单,无需引入额外的依赖。
无中间件依赖: Spring Event不依赖于任何消息中间件,适用于小型项目或者简单的消息通信场景。
模块解耦: Spring Event可以帮助实现模块之间的解耦,提高系统的灵活性和可维护性。
缺点:
单点问题: Spring Event是在单个应用内部的事件通知机制,如果应用崩溃或者重启,事件将会丢失。
不支持分布式: Spring Event只能在单个应用内部传递消息,不支持分布式环境下的消息传递。
性能问题: Spring Event在大规模消息通信场景下可能会存在性能问题,因为它是同步执行的,消息发布者需要等待所有订阅者处理完消息后才能继续执行