参考
掘金:Spring 事件发布及监听
作者:阿劲
掘金:观察者模式 vs 发布订阅模式,千万不要再混淆了
作者:战场小包
CSDN:SpringBoot事件监听机制及观察者/发布订阅模式详解
作者:鸨哥学JAVA
什么是观察者模式?
当对象之间存在一对多的依赖关系时,其中一个对象的状态发生改变,所有依赖它的对象都会收到通知,这就是观察者模式。
代码实现
一、注解方式实现
- 创建事件类
PayEvent
,继承ApplicationEvent
。
/**
* 支付事件
*/
@Getter
public class PayEvent extends ApplicationEvent {
private Order order;
public PayEvent(Object source, Order order) {
super(source);
this.order = order;
}
}
- 编写监听类
PayListener
,类上添加注解@Component
,添加事件处理方法sendMsg
、sendWeChat
,并在方法上添加注解@EventListener
。
- 如需异步执行方法,还需要添加注解
@Async
,参考下方内容拓展:异步事件
。此处若使用@Async()
不指定线程池,使用的是默认线程池SimpleAsyncTaskExecutor
.
/**
* 支付事件监听
*/
@Component
public class PayListener {
@EventListener
@Async("taskExecutor")
public void sendMsg(PayEvent payEvent) {
Object source = payEvent.getSource();
System.out.println(JSON.toJSONString(source));
Order order = payEvent.getOrder();
System.out.println("发送短信" + order.getPhone() + "当前线程" + Thread.currentThread().getId()
+ Thread.currentThread().getName());
}
@EventListener
@Async("pool")
public void sendWeChat(PayEvent payEvent) {
Order order = payEvent.getOrder();
System.out.println("发送微信通知" + order.getWeChat() + "当前线程" + Thread.currentThread().getId()
+ Thread.currentThread().getName());
}
}
- 事件监听,注入依赖
ApplicationContext
,new一个事件类PayEvent
,设置参数,最后使用applicationContext.publishEvent(payEvent)
监听事件。
// 此处用的是setter注入,也可通过在字段上添加@Autowired,或者构造器注入依赖。
// 依赖注入方式可参考: https://juejin.cn/post/7145374960784162829
private ApplicationContext applicationContext;
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@GetMapping("/payEvent")
public void payEvent() {
Order order = new Order();
order.setPhone("123456");
order.setWeChat("wexin");
PayEvent payEvent = new PayEvent(this, order);
System.out.println("开始事件,当前线程"+Thread.currentThread().getId());
applicationContext.publishEvent(payEvent);
}
拓展:异步事件
Spring
中的事件默认情况下是同步的,发布者线程会进入阻塞状态,直到所有的监听器处理完事件。如果想让事件监听异步执行,需要在监听器上添加@Async
, 同时主启动类上添加@EnableAsync
注解。
另外,还可以自定线程池,在自定义线程池上添加@EnableAsync
注解,这时启动类上就可以不用添加了。
@Bean({"taskExecutor", "pool"})
默认情况下bean的名称和方法名称相同,此处使用name属性来指定{"taskExecutor", "pool"}
,这里指定了多个名称,可以只指定一个,如@Bean("pool")
。
/**
* 线程池配置
*/
@Configuration
@EnableAsync()
public class AsyncConfig {
@Bean({"taskExecutor", "pool"})
public AsyncTaskExecutor taskExecutor() {
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("consumer-queue-thread-%d").build();
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 线程池维护线程的最少数量
executor.setCorePoolSize(5);
// 线程池维护线程的最大数量
executor.setMaxPoolSize(10);
// 缓存队列
executor.setQueueCapacity(25);
//线程名
executor.setThreadFactory(namedThreadFactory);
// 线程池初始化
executor.initialize();
return executor;
}
}
事务绑定事件
事务绑定事件@TransactionalEventListener
是 @EventListener
的扩展,Spring
允许将事件监听器绑定到事务的某个阶段。
可参考
掘金:Spring 事件发布及监听
作者:阿劲