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> {@Overridepublic 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 {@Resourceprivate ApplicationContext applicationContext;@Overridepublic 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在大规模消息通信场景下可能会存在性能问题,因为它是同步执行的,消息发布者需要等待所有订阅者处理完消息后才能继续执行