暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

浅谈spring中的事件机制

轻聊浅谈闲码 2020-12-14
1189

事件机制

事件采用的是观察者模式。

  • 同步事件:在一个线程里,按顺序执行业务,做完一件事再去做下一件事。

  • 异步事件:在一个线程里,做一个事的同时,可以另起一个新的线程执行另一件事,这样两件事可以同时执行。

spring事件发送监听涉及三个部分

  • ApplicationEvent:表示事件本身,自定义事件需要继承该类,可以用来传递数据。

  • ApplicationEventPublisherAware:事件发送器,通过实现这个接口,来触发事件。

  • ApplicationListener:事件监听器接口,事件的业务逻辑封装在监听器里面。

事件原理

发布者调用applicationEventPublisher.publishEvent(msg)将事件发送给EventMultiCaster,EventMultiCaster中注册着所有的Listener,然后就会根据事件类型决定转发给那个Listener。

  1. public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {

  2. // 这个是用来根据event的类型找到合适的listener的

  3. ResolvableType type = (eventType != null ? eventType :resolveDefaultEventType(event));

  4. for (final ApplicationListener<?> listener : getApplicationListeners(event, type)){

  5. Executor executor = getTaskExecutor();

  6. // executor不是空的时候会在executor中激活listener

  7. if (executor != null) {

  8. executor.execute(new Runnable() {

  9. @Override

  10. public void run() {

  11. invokeListener(listener, event);

  12. }

  13. });

  14. } else {

  15. // 否则就直接在当前调用线程中激活listener

  16. invokeListener(listener, event);

  17. }

  18. }

  19. }

默认SimpleApplicationEventMulticaster在创建SimpleApplicationEventMulticaster时,executor是null,所以默认情况下所有的listener 的onApplicationEvent是直接在当前线程(事件发布者所在线程)中调用,所以如果onApplicationEvent有阻塞操作也会导致事件发布者被阻塞,后续的其他listener也会被阻塞无法调用。

自定义multicaster

  1. <!-- 使用线程池运行listener -->

  2. <bean id="executorService" class="java.util.concurrent.Executors" factory-method="newCachedThreadPool" />


  3. <bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">

  4. <property name="taskExecutor" ref="executorService" />

  5. </bean>

  6. <bean id="applicationEventAsyncMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">

  7. <property name="taskExecutor">

  8. <bean class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">

  9. <property name="corePoolSize" value="5"/>

  10. <property name="keepAliveSeconds" value="3000"/>

  11. <property name="maxPoolSize" value="50"/>

  12. <property name="queueCapacity" value="200"/>

  13. </bean>

  14. </property>

  15. </bean>

备注:如果加上taskExecutor,则使用异步方式执行,否则为同步方式


事件传播

当我们监听一个事件处理完成时,还需要发布另一个事件,一般我们想到的是调用ApplicationEventPublisher#publishEvent发布事件方法,但Spring提供了另一种更加灵活的新的事件继续传播机制,监听方法返回一个事件,也就是方法的返回值就是一个事件对象。

也可以使用注解
@Async
@EventListener
从Spring 4.2开始,框架提供了一个新的@TransactionalEventListener注解,它是@EventListener的扩展,允许将事件的侦听器绑定到事务的一个阶段。绑定可以进行以下事务阶段:

  1. AFTER_COMMIT(默认的):在事务成功后触发

  2. AFTER_ROLLBACK:事务回滚时触发

  3. AFTER_COMPLETION:事务完成后触发,不论是否成功

  4. BEFORE_COMMIT:事务提交之前触发

  1. @TransactionalEventListener(phase = BEFORE_COMMIT)

  2. public void txEvent(Order order) {

  3. System.out.println("事物监听");

  4. }

事件类一定要继承ApplicationEvent吗?

当然不是,我们还可以自定义泛型类实现事件调度
1.写个通用泛型类事件

  1. public class GenericEvent<T> {


  2. private T data;

  3. private boolean success;


  4. public GenericEvent(T data,boolean success){

  5. this.data = data;

  6. this.success = success;

  7. }


  8. public T getData() {

  9. return data;

  10. }


  11. public void setData(T data) {

  12. this.data = data;

  13. }


  14. public boolean isSuccess() {

  15. return success;

  16. }


  17. public void setSuccess(boolean success) {

  18. this.success = success;

  19. }

  20. }

2.写个具体类型的事件

  1. public class OrderGenericEvent extends GenericEvent<Order> {

  2. public OrderGenericEvent(Order data, boolean success) {

  3. super(data, success);

  4. }

  5. }

3.在service的方法中发布事件

  1. applicationEventPublisher.publishEvent(new OrderGenericEvent(order,true));

4.事件处理

  1. @Componentpublic class OrderGenericEventListener {

  2. @EventListener(condition = "#event.success")

  3. public void orderListener(GenericEvent<Order> event){

  4. System.out.println(this.getClass().getName()+"--处理泛型条件事件。。。");

  5. }

  6. }

测试结果表明,成功处理了事件。
success为true时,我们把发布事件的代码改为如下内容再测试,则不会收到事件通知。

  1. applicationEventPublisher.publishEvent(new OrderGenericEvent(order,false));

配置事件监听器的一些方法

  1. SpringApplication.addListeners 添加监听器

  2. 把监听器纳入到spring容器中管理

  3. 使用context.listener.classes配置项配置

  4. 使用@EventListener注解,在方法上面加入@EventListener注解,且该类需要纳入到spring容器中管理

spring和springboot已经定义好的事件

spring的事件

springboot的事件

文章转载自轻聊浅谈闲码,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论