Spring事件机制详解

1、前言

  说来惭愧,对应Spring事件机制以前只知道实现 ApplicationListener 接口,就能够基于Spring自带的事件作一些事情(如ContextRefreshedEvent),可是最近看公司的wiki基于Spring事件的领域驱动才发现原来还有这么多东西。html

2、订阅/发布(观察者模式)

2.1简介

Spring是基于事件驱动模型的,咱们经常使用的MQ就是基于观察者模式设计的。
事件驱动模型也就是咱们常说的观察者,或者发布-订阅模型;理解它的几个关键点:java

  1. 首先是一种对象间的一对多的关系;最简单的如交通讯号灯,信号灯是目标(一方),行人注视着信号灯(多方)。
  2. 当目标发送改变(发布),观察者(订阅者)就能够接收到改变。
  3. 观察者如何处理(如行人如何走,是快走/慢走/不走,目标不会管的),目标无需干涉;因此就松散耦合了它们之间的关系。

Java API实现和自定义实现观察者模式:git

Java提供了两个接口java.util.Observablejava.util.Observer,代码可参考https://github.com/2YSP/design-pattern/tree/master/src/cn/sp/observergithub

3、Spring类图分析

 

类图
类图

 

事件

  1. ApplicationEvent 继承自 JDK 的 EventObject,JDK要求全部事件将继承它,并经过source获得事件源,好比AWT事件体系也是继承它。
  2. 系统默认提供了以下ApplicationEvent事件实现:

 

类图
类图

 

事件发布者

具体表明者是:ApplicationEventPublisherApplicationEventMulticaster,系统默认提供了以下实现:spring

 

ApplicationEventPublisher类图
ApplicationEventPublisher类图

 

 

ApplicationEventMulticaster类图
ApplicationEventMulticaster类图

 

  1. ApplicationContext 接口继承了 ApplicationEventPublisher,并在 AbstractApplicationContext 实现了具体代码,实际执行是委托给ApplicationEventMulticaster(能够认为是多播)
    代码以下:

 

AbstractApplicationContext发布事件代码
AbstractApplicationContext发布事件代码

 

 

ApplicationEventMulticaster初始化逻辑
ApplicationEventMulticaster初始化逻辑

2.根据上面代码能够看出 ApplicationContext 会自动在本地容器找一个ApplicationEventMulticaster的实现,若是没有本身new一个SimpleApplicationEventMulticaster,其中SimpleApplicationEventMulticaster发布事件的代码以下:

 

 

发布事件方法
发布事件方法

能够看出若是给它一个executor,它就能够实现异步发布事件了,不然就是同步发送。

 

监听器

ApplicationListener 接口提供了onApplicationEvent方法,可是咱们须要在该方法实现内部判断事件类型来处理,也没有提供按顺序触发监听器的语义,因此Spring提供了另外一个接口,SmartApplicationListener:bash

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
     //若是实现支持该事件类型 那么返回true
    boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
  
        //若是实现支持“目标”类型,那么返回true
    boolean supportsSourceType(Class<?> sourceType);
       
     //顺序,即监听器执行的顺序,值越小优先级越高
    int getOrder();

}

源码分析到这里,下面说说怎么使用。app

4、简单实现(实现ApplicationListener接口)

场景是咱们保存一个订单后发布事件通知,以便作一些其余操做好比锁定商品。
订单实体类:框架

public class Order {

    private String orderNo;

    private String orderStatus;

    private String goods;

    private Date createTime;
    
    //省略get、set、toString方法
}

订单建立事件OrderCreateEvent异步

public class OrderCreateEvent extends ApplicationEvent {

    private final Order order;

    public OrderCreateEvent(Object source,Order order) {
        super(source);
        this.order = order;
    }

    public Order getOrder(){
        return order;
    }
}

OrderServiceide

  1. @Service 
  2. public class OrderService implements ApplicationEventPublisherAware
  3.  
  4. private ApplicationEventPublisher applicationEventPublisher; 
  5.  
  6. /** 
  7. * 订单保存 
  8. */ 
  9. public void save()
  10. String orderNo = getOrderNo(); 
  11. Order order = new Order(); 
  12. order.setOrderNo(orderNo); 
  13. order.setOrderStatus("待付款"); 
  14. order.setCreateTime(new Date()); 
  15. order.setGoods("手机"); 
  16. System.out.println("订单保存成功:" + order.toString()); 
  17.  
  18. //发布事件 
  19. applicationEventPublisher.publishEvent(new OrderCreateEvent(this,order)); 
  20. System.out.println("================"); 
  21.  
  22.  
  23. private String getOrderNo()
  24. String millis = String.valueOf(System.currentTimeMillis()); 
  25. String str = millis.substring(millis.length() - 7, millis.length() - 1); 
  26. return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + str; 
  27.  
  28.  
  29. @Override 
  30. public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher)
  31. this.applicationEventPublisher = applicationEventPublisher; 

监听器OrderCreateEventListener

  1. @Component 
  2. public class OrderCreateEventListener implements ApplicationListener<OrderCreateEvent>
  3.  
  4.  
  5. @Override 
  6. public void onApplicationEvent(OrderCreateEvent event)
  7. System.out.printf(this.getClass().getName()+ " -- ApplicationListener 接口实现,订单号[%s]:,锁定商品[%s]\n"
  8. event.getOrder().getOrderNo(), event.getOrder().getGoods()); 

运行测试类:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationEventDemoApplicationTests {

    @Autowired
    OrderService orderService;

    @Test
    public void contextLoads() {
        orderService.save();
    }

}

控制台打印以下则表示成功实现了监听。

订单保存成功:Order{orderNo='20190601983801', orderStatus='待付款', goods='手机', createTime=Sat Jun 01 00:23:58 CST 2019}
2019-06-01 00:23:58.069  INFO 15060 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
cn.sp.listener.OrderCreateEventListener -- ApplicationListener 接口实现,订单号[20190601983801]:,锁定商品[手机]
================

5、注解驱动@EventListener

接着上面的,自定义的监听器必定要实现ApplicationListener接口吗?不是,Spring还提供了注解的方式 @EventListener,使用示例以下:

@Component
public class OrderCreateEventListenerAnnotation {

    @EventListener
    public void createOrderEvent(OrderCreateEvent event){
        System.out.println(this.getClass().getName()+"--订单建立事件,@EventListener注解实现,orderNo:"+event.getOrder().getOrderNo());
    }
}

注意:@EventListener有个condition属性,还能够支持条件判断(定义布尔SpEL表达式),只有知足条件才会触发,后面泛型支持那里有示例。

6、异步事件

上面的监听事件都是同步触发的,若是想异步的怎么办?
只须要两步:

  1. 启动类上添加 @EnableAsync注解,开启异步支持。
  2. 监听方法上添加 @Async注解
@Async
    @EventListener
    public void createOrderEvent(OrderCreateEvent event){
        System.out.println(this.getClass().getName()+"--订单建立事件,@EventListener注解实现,orderNo:"+event.getOrder().getOrderNo());
    }

7、泛型支持

事件类必定要继承ApplicationEvent吗?
固然不是,咱们还能够自定义泛型类实现事件调度(这个是我认为最厉害的地方了)。

  1. 写个通用泛型类事件
/** * 能够自定义泛型类实现事件调度 * Created by 2YSP on 2019/5/30. */
public class GenericEvent<T> {

    private  T data;
    private boolean success;

    public GenericEvent(T data,boolean success){
        this.data = data;
        this.success = success;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }
}
  1. 写个具体类型的事件
public class OrderGenericEvent extends GenericEvent<Order> {

    public OrderGenericEvent(Order data, boolean success) {
        super(data, success);
    }
}
  1. 在OrderService的save()方法中发布事件

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

  1. 事件处理
@Component
public class OrderGenericEventListener {

    @EventListener(condition = "#event.success")
    public void orderListener(GenericEvent<Order> event){
        System.out.println(this.getClass().getName()+"--处理泛型条件事件。。。");
    }
}

测试结果代表,成功处理了事件。

 

success为true时
success为true时

咱们把发布事件的代码改成以下内容再测试,则不会收到事件通知。
applicationEventPublisher.publishEvent(new OrderGenericEvent(order,false));

 

8、事件传播机制

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

public void save(){
        String orderNo = getOrderNo();
        Order order = new Order();
        order.setOrderNo(orderNo);
        order.setOrderStatus("待付款");
        order.setCreateTime(new Date());
        order.setGoods("手机");
        System.out.println("订单保存成功:" + order.toString());

        //发布事件
// applicationEventPublisher.publishEvent(new OrderCreateEvent(this,order));
        applicationEventPublisher.publishEvent(order);
// applicationEventPublisher.publishEvent(new OrderGenericEvent(order,true));
        System.out.println("================");

    }

订单监听器

@Component
public class OrderListener {

    @EventListener
    public void orderListener(Order order){
        System.out.println(this.getClass().getName() + " -- 监听一个订单");
    }

    @EventListener
    public OrderCreateEvent orderReturnEvent(Order order){
        System.out.println(this.getClass().getName() + " -- 监听一个订单,返回一个新的事件 OrderCreateEvent");
        return new OrderCreateEvent(this,order);
    }
}

启动单元测试,就会发现OrderCreateEventListener也被触发了。

 

enter description here
enter description here

固然还能够返回多个事件,再也不举例。

 

9、事物事件@TransactionalEventListener

从Spring 4.2开始,框架提供了一个新的@TransactionalEventListener注解,它是@EventListener的扩展,容许将事件的侦听器绑定到事务的一个阶段。绑定能够进行如下事务阶段:

  1. AFTER_COMMIT(默认的):在事务成功后触发
  2. AFTER_ROLLBACK:事务回滚时触发
  3. AFTER_COMPLETION:事务完成后触发,不管是否成功
  4. BEFORE_COMMIT:事务提交以前触发
@TransactionalEventListener(phase = BEFORE_COMMIT)
public void txEvent(Order order) {
    System.out.println("事物监听");
}

上面代码的意思是仅当存在事件生成器正在运行且即将提交的事务时,才会调用此侦听器。而且,若是没有正在运行的事务,则根本不发送事件,除非咱们经过将fallbackExecution 属性设置为true来覆盖它 ,即 @TransactionalEventListener(fallbackExecution = true)

10、总结

基于事件驱动模型能够很方便的实现解耦,提升代码的可读性和可维护性,代码地址:https://github.com/2YSP/application-event-demo
疑问: 泛型支持那里若是不写一个类继承通用泛型事件,就跑不通这是为何呢?
若是有人知道请告诉我,很是感谢。
参考:https://blog.csdn.net/sun_shaoping/article/details/84067446

相关文章
相关标签/搜索