Spring的自定义事件广播机制, 能非常好的将我们的业务代码解耦, 提升代码的可读性, 学习一下, 没坏处
1. 最基础的使用
-
自定义事件类
@Getter
public class MyEvent extends ApplicationEvent { //也可以不继承
public LiveRoomTimeChangeEvent(Object source) {
super(source);
}
}
定义事件监听器
@Component
public class MyEventListener {
@EventListener
public void consumeEvent(MyEvent myEvent) {
//doSomething
}
}
-
广播事件
@Component
public class A {
@Autowired
private ApplicationContext applicationContext;
public void methodA() {
//广播事件
applicationContext.publishEvent(new MyEvent(this));
}
}
2. 通过实现接口的方式注册事件监听器
在前面的例子中, 我们使用了
@EventListener
注解的方式声明监听器
其实Spring还提供了另一种方式实现监听器
@Component
public class MyEventListener2 implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println(Thread.currentThread().getName());
}
}
这样写的优势是, 用泛型规范了代码, 可以在编译时发现一些错误
3. 异步消费
默认情况是不会异步消费事件的, 我们需要提前配置才能让事件异步的进行消费
3.1 默认情况同步消费
默认消费事件与广播事件, 是在同一个线程完成的
- 看下图的调试结果
-
3.2 实现异步消费
org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
- 通过观察源码可以看到 必须提前设置
taskExecutor
才能异步地消费事件
- 我们这需要下面几件事
- 自定义事件分发器
ApplicationEventMulticaster
- 自定义线程池
- 将自定义线程池设置到事件分发器中
@Configuration
public class AsyncEventConfig implements BeanPostProcessor {
@Bean("applicationEventMulticaster") //注意必须是这个名字
public ApplicationEventMulticaster applicationEventMulticaster() { //手动创建事件广播器
SimpleApplicationEventMulticaster result = new SimpleApplicationEventMulticaster();
result.setTaskExecutor(this.eventExecutor().getObject());//设置异步线程池
return result;
}
@Bean
public ThreadPoolExecutorFactoryBean eventExecutor() {//创建线程池
ThreadPoolExecutorFactoryBean eventExecutor = new ThreadPoolExecutorFactoryBean();
eventExecutor.setThreadNamePrefix("eventExecutor-");
eventExecutor.setCorePoolSize(3);
return eventExecutor;
}
}
- 打印线程看看效果
使用这种方法会有一个问题, 就是所有的事件全部变成异步的了, 如果希望同步的消费反而不行了
而且所有的事件, 全在一个线程池中, 这样就没有优先级的区分, 不是非常完美
3.3 使用@Async实现异步调用
- 启用Spring的Async功能
并注册多个线程池
@EnableAsync
@Configuration
public class AsyncConfig {
//这里可以初始化多个线程池, 让@Async执行的时候指定线程池
@Bean("一号线程池")
public ThreadPoolExecutorFactoryBean eventExecutor1() {
ThreadPoolExecutorFactoryBean eventExecutor = new ThreadPoolExecutorFactoryBean();
eventExecutor.setThreadNamePrefix("一号线程池-");
eventExecutor.setCorePoolSize(3);
return eventExecutor;
}
@Bean("二号线程池")
public ThreadPoolExecutorFactoryBean eventExecutor2() {
ThreadPoolExecutorFactoryBean eventExecutor = new ThreadPoolExecutorFactoryBean();
eventExecutor.setThreadNamePrefix("二号线程池-");
eventExecutor.setCorePoolSize(3);
return eventExecutor;
}
}
注册监听器的时候使用 @Async
并指定执行的线程池
@Component
public class MyEventListener1 {
@Async("一号线程池")
@EventListener
public void consumeEvent(MyEvent myEvent) {
System.out.println(Thread.currentThread().getName());
}
}
@Component
public class MyEventListener2 implements ApplicationListener<MyEvent> {
@Async("二号线程池")
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println(Thread.currentThread().getName());
}
}
看看推送事件之后的执行结果
这里注意一下, 如果使用 @Async 的方式, 就不要自己注册 ApplicationEventMulticaster 了, 浪费资源