Spring中观察者模式的应用

1. 通过@EventListener监听ApplicationEvent

Spring里的ApplicationListen相当于观察者;ApplicationEventPublisher可看作被观察对象。
当需要实现通知多个观察者的操作时可使用Spring里的ApplicationEventPublishe。观察者的操作可同步也可异步。

@Service
public class MyService {
    /**
     * Spring4.2之后,ApplicationEventPublisher 自动被注入到容器中,不再需要显示实现Aware接口。
     */
    @Autowired
    private ApplicationEventPublisher publisher;

    public void doSomething(){
        System.out.println(Thread.currentThread().getName()+ ":send the msg");
        //发布通知
        publisher.publishEvent(new MyEvent("content"));
    }
}
/**
 * 自定义事件
 */
public class MyEvent extends ApplicationEvent {
    public MyEvent(Object source) {
        super(source);
    }
}
/**
 * 自定义监听器(观察者)
 */
@Component
public class MyListener   {
    @EventListener
    public void reciveMsg(MyEvent myEvent){
        String msg = (String) myEvent.getSource();
        System.out.println(Thread.currentThread().getName()+ ": recive msg --"+msg);
    }
}

运行结果:service调用publisher的publishEven方法会将ApplicationEvent对象传给Multicast对象,Multicast对象可以根据ApplicationEvent拿到对应的监听器集合,迭代这个集合,执行每个监听器的操作。 这个过程是同步的,也就是说service必须等待观察者操作完毕才能继续运行。
当需要观察者异步执行时可做以下修改:
在容器中添加一个自定义的Executor,在启动类上添加@EnableAsync注解

@Configuration
@ComponentScan
@EnableAsync
public class MyConfig {
    @Bean("myThreadPool")
    public Executor getExecutor() {
        ThreadFactory namedThreadFactory = r -> {
            Thread thread = new Thread(r);
            thread.setName("myThreadPool");
            return thread;
        };
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 线程池维护线程的最少数量
        executor.setCorePoolSize(5);
        // 线程池维护线程的最大数量
        executor.setMaxPoolSize(10);
        // 缓存队列
        executor.setQueueCapacity(25);
        //线程名
        executor.setThreadFactory(namedThreadFactory);
        // 线程池初始化
        executor.initialize();
        return executor;
    }
}

在监听器方法上添加@Async注解,指定自己定义的Executor

/**
 * 自定义监听器(观察者)
 */
@Component
public class MyListener   {
    @Async("myThreadPool")
    @EventListener
    public void reciveMsg(MyEvent myEvent){
        String msg = (String) myEvent.getSource();
        System.out.println(Thread.currentThread().getName()+ ": recive msg --"+msg);
    }
}

2. 通过@TransactionalEventListener监听事务提交

可用在事务结束后需要发送mq的场景,防止事务在生产者发送消息后才提交造成的问题

在Service的需要事务控制的方法最后发送自定义事件

@Service
public class MyService {
    /**
     * Spring4.2之后,ApplicationEventPublisher 自动被注入到容器中,不再需要显示实现Aware接口。
     */
    @Autowired
    private ApplicationEventPublisher publisher;

    //添加事务注解
    @Transactional(rollbackFor = Exception.class)
    public void doSomething(){
        System.out.println(Thread.currentThread().getName()+ ":send the msg");
        //发布通知
        publisher.publishEvent(new MyEvent("content"));
    }
}

定义监听器
原理:publisher发送事件的方法会通过传入的事件参数查找与之对应的监听器对象,将其注册到一个容器中,当执行完事务提交操作后,会遍历容器中的监听器,执行相应的处理方法。

@Component
public class TransactionEventListener {

    /**
     * AFTER_COMMIT是事务提交后执行,默认就是这个
     * fallbackExecution=true则会在没有事务时也执行该方法,不然只有加上事务才会监听,切记。默认是false
     * @param message
     * @throws Exception
     */
    @TransactionalEventListener(fallbackExecution = true,phase=TransactionPhase.AFTER_COMMIT)
    public void handleMessageSend(TestMqDTO message) throws Exception{
        System.out.println("发送消息");
    }
  
}

观察者模式的好处
1.被观察者不需要知道观察者是谁,也不知道有多少观察者
2.增加或减少观察者,都不会影响被观察者的逻辑