观察者模式-事件发布与监听

149 阅读2分钟

参考

掘金:Spring 事件发布及监听
作者:阿劲

掘金:观察者模式 vs 发布订阅模式,千万不要再混淆了
作者:战场小包

CSDN:SpringBoot事件监听机制及观察者/发布订阅模式详解
作者:鸨哥学JAVA

什么是观察者模式?

当对象之间存在一对多的依赖关系时,其中一个对象的状态发生改变,所有依赖它的对象都会收到通知,这就是观察者模式。

代码实现

一、注解方式实现

  1. 创建事件类PayEvent,继承ApplicationEvent
/**
 * 支付事件
 */
@Getter
public class PayEvent extends ApplicationEvent {
    private Order order;

    public PayEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
}
  1. 编写监听类PayListener,类上添加注解@Component,添加事件处理方法sendMsgsendWeChat,并在方法上添加注解@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());
    }
}
  1. 事件监听,注入依赖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 事件发布及监听
作者:阿劲

下次再见