介绍
Spring Event是Spring框架中的一个事件机制,主要用于实现应用程序内部的事件传递与处理,它允许不同组件之间通过发布-订阅机制进行解耦通信,比如用户注册,订单创建,订单状态变更,通知等。Spring Event也是观察者模式的一种实现,所以Spring Event主要是由三部分来实现,分别是:事件(ApplicationEvent)、监听器(ApplicationListener)和事件发布操作。
应用场景
- 用户行为跟踪:可以使用 Spring Event 来跟踪用户在应用程序中的行为,例如登录、注册、购买等操作,从而实现用户行为分析和个性化推荐等功能。
- 数据同步更新:可以使用 Spring Event 来实现数据的同步更新,例如,在分布式系统中,当某个节点的数据发生变化时,可以触发事件并将事件传递给其他节点,从而实现数据的同步更新。
- 异步任务处理:可以使用 Spring Event 来实现异步任务的处理,例如,在 Web 应用程序中,可以使用异步 Servlet 和 Spring Event 来处理异步请求,从而提高应用程序的性能和并发能力。
- 领域事件处理:可以使用 Spring Event 来实现领域事件的处理,例如,在订单管理系统中,可以使用 Spring Event 来处理订单状态的变化,从而触发相关的业务逻辑。
- 消息通知和推送:可以使用 Spring Event 来实现消息的通知和推送,例如,在在线聊天应用程序中,可以使用 Spring Event 来实现消息的推送和实时通知。
Spring Event 可以在应用程序中实现事件的传递和处理,提高应用程序的性能、并发能力和灵活性,同时也能够实现一些复杂的业务逻辑和功能
优点
在代码中直接调用其他业务的接口是一种紧密耦合的方式,这会增加代码的复杂度、降低代码的可维护性和可扩展性。而使用 Spring Event 可以将不同业务之间的耦合度降到最低,提高应用程序的灵活性和可扩展性。具体来说,使用 Spring Event 有以下几个好处
- 松散耦合:使用 Spring Event 可以将不同业务之间的耦合度降到最低,每个业务只需要关注自己感兴趣的事件,而不需要了解其他业务的具体实现。
- 解耦业务逻辑:使用 Spring Event 可以将业务逻辑和事件处理逻辑解耦,每个业务只需要关注自己的业务逻辑,而不需要关注事件的处理逻辑,从而提高代码的可维护性和可扩展性。
- 提高代码复用性:使用 Spring Event 可以将事件的处理逻辑封装成可重用的组件,多个业务可以共享同一个事件处理逻辑,从而提高代码的复用性。
- 降低测试成本:使用 Spring Event 可以将不同业务之间的测试隔离开来,每个业务只需要关注自己的测试,而不需要关注其他业务的测试,从而降低测试成本。
- 提高代码可读性:使用 Spring Event 可以将事件的触发和处理逻辑清晰地分离出来,从而提高代码的可读性和可理解性。
实例-同步事件
1、定义用户注册事件
注意,自定义事件需要继承自ApplicationEvent类
public class UserRegisterEvent extends ApplicationEvent {
private User user;
public UserRegisterEvent(Object source, User user){
super(source);
this.user = user;
}
}
2、定义用户事件监听
注意:事件监听需要在方法上面标注@EventListener注解
@Component
public class UserEventListener {
@Resource
private MailService mailService;
@Resource
private PointService pointService;
@EventListener
public void userRegisterEventLister(UserRegisterEvent userRegisterEvent){
System.out.println("监听到用户注册");
//进行邮件注册通知
mailService.sendRegisterSuccessMail();
//进行积分发放
pointService.userRegisterPoint();
}
}
3、用户注册成功并发布注册成功事件
注意:在springboot中需要使用ApplicationEventPublisher类来进行发布用户注册事件
@Service
public class UserService {
@Resource
private ApplicationEventPublisher applicationEventPublisher;
public void register(User user){
System.out.println("用户注册成功");
// 发布用户注册成功事件
applicationEventPublisher.publishEvent(new UserRegisterEvent(this, user));
}
}
4、用户注册调用与验证
@RestController
public class HomeController {
@Resource
private UserService userService;
@PostMapping("/register")
public String register(@RequestBody User user){
userService.register(user);
return "register success";
}
}
访问输出日志为:
用户注册成功
监听到用户注册
发送注册成功邮件
用户注册成功发放积分
这样一个用户注册的应用场景就完成,使用了Spring Event来对主要用户注册主要逻辑与发送邮件,发送积分等次要逻辑进行解耦,从而形成了次要逻辑可拔插的效果。
实例-异步事件
上面实现了同步事件的逻辑,那这里就会出现一个问题,如果次要逻辑是否成功都不影响主逻辑的执行或者次要逻辑是较为耗时的操作,那么用户的请求等待时间将会比较长,因此可以将次要逻辑进行异步处理,将继续使用上面的例子,将其改造为异步事件,具体实现如下。
1、开启异步功能
需要在SpringBoot的启动类加上开启异步任务的注解:@EnableAsync
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2、自定义异步线程池
新建 一个异步线程池的配置类,AsyncConfig
@Configuration
public class AsyncConfig implements AsyncConfigurer {
private static final Logger log = LoggerFactory.getLogger(AsyncConfig.class);
/**
* 自定义异步线程池,如不重写则使用默认线程池
* @return
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//设置核心线程数
taskExecutor.setCorePoolSize(5);
//设置最大线程数
taskExecutor.setMaxPoolSize(10);
//队列大小
taskExecutor.setQueueCapacity(15);
//设置线程前缀名
taskExecutor.setThreadNamePrefix("async-thread-");
//线程初始化
taskExecutor.initialize();
return taskExecutor;
}
/**
* 捕捉IllegalArgumentException异常
* @return
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncException();
}
class AsyncException implements AsyncUncaughtExceptionHandler{
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
log.info("TASK EXCEPTION MESSAGE - " + ex.getMessage());
log.info("METHOD NAME - " + method.getName());
for (Object param : params) {
log.info("Parameter value - " + param);
}
}
}
}
注:也可以不使用自定义异步线程池,使用默认的线程池也是可以的,只不过默认的线程池的最大线程数为:Integer.MAX_VALUE,这其实是不合理,因此通常情况下,都使用自定义异步任务线程来使用。
3、事件监听执行异步任务
在事件监听事件处理的方法上面,添加注解:@Async,即可以将该事件变为异步执行任务,代码如下:
@Component
@Log4j2
public class UserEventListener {
@Resource
private MailService mailService;
@Resource
private PointService pointService;
@Async
@EventListener
public void userRegisterEventLister(UserRegisterEvent userRegisterEvent){
log.info("监听到用户注册");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//进行邮件注册通知
mailService.sendRegisterSuccessMail();
//进行积分发放
pointService.userRegisterPoint();
}
}
运行输出:
用户注册成功
2024-01-11 19:24:53.009 INFO 33219 --- [ async-thread-2] c.e.demo.listener.UserEventListener : 监听到用户注册
2024-01-11 19:24:57.011 INFO 33219 --- [ async-thread-2] com.example.demo.service.MailService : 发送注册成功邮件
2024-01-11 19:24:57.012 INFO 33219 --- [ async-thread-2] com.example.demo.service.PointService : 用户注册成功发放积分
到这里为止,使用Spring Event的基本使用方式都已完成。
原创文章,作者:jiafegn,如若转载,请注明出处:https://www.techlearn.cn/archives/1999