SpringBoot深度探究(六)事件编程模型(Listener-Event)详解

56 篇文章 3 订阅
12 篇文章 2 订阅

前言

前两篇博客【观察者设计模式的演进】【JDK观察者模式探究】深度讲了观察者模式的实现和作用,相信很多同学都知道SpringBoot是基于Spring Framework的一套东西做出来的。之所以SpringBoot能够做到非常简便的启动,很大一部分功劳应该属于Spring Framework中提供的Listener-Event功能。所以这篇博客就是要看下Spring的Listener-Event到底是有什么功能,以及它在SpringBoot的启动中扮演了什么角色。更多Spring内容进入【Spring解读系列目录】

Spring Events

Spring当中的事件通过org.springframework.context.ApplicationEvent实例来表示。这个抽象类继承扩展了java.util.EventObject,通过使用EventObject中的getSource()方法,可以很容易地获得所发生的给定事件的对象。SpringBoot中事件大致有两种类型:

第一种:与ApplicationContext相关联

所有这种类型的事件都继承自org.springframework.context.event.ApplicationContextEvent类。凡是继承了ApplicationContextEvent类都可以被应用于由org.springframework.context.ApplicationContext引发的事件(简言之,就是那些构造函数传入的是ApplicationContext类型的参数)。通过这种方式就可以直接通过应用程序上下文的生命周期来得到所发生的事件。本质上来说SpringBoot一共有四个分支事件。
• ContextStartedEvent:在上下文启动时被启动。
• ContextStoppedEvent:当上下文停止时启动。
• ContextRefreshedEvent:当上下文被刷新时被启动。
• ContextClosedEvent:当上下文被关闭时被启动。

在这里插入图片描述

第二种:与request 请求相关联

org.springframework.web.context.support.RequestHandledEvent实例来表示,当在ApplicationContext中处理请求时,这些实例就会被启动。

本篇将会主要对与ApplicationContext相关联的应用事件进行一个举例说明。

事件的定义

之前的例子里,我们举例开花这个事件,其实可以抽象出来作为一个事件类,所有Flower能够表示的行为都实现这个事件类。因为Flower不仅可以开花,还可以凋零,还可以生虫等等。而两个主人翁则对不同的事件有不同的反应。那么SpringBoot中就是对这些事件做了一个抽象,凡是事件都必须继承自ApplicationEvent

public abstract class ApplicationEvent extends EventObject {
   private static final long serialVersionUID = 7099057708183571937L;
   private final long timestamp;
   public ApplicationEvent(Object source) {
      super(source);
      this.timestamp = System.currentTimeMillis();
   }
   public final long getTimestamp() {
      return this.timestamp;
   }
}

从源码来看这个类需要接收一个参数Object source,这个参数就是所谓的事件源。何为事件源呢?就是发生事件的东西,也就是我们上述的Flower。我们可以通过其父类EventObject提供的getSource()方法去获取这个数据源。

public class EventObject implements java.io.Serializable {
    /**注:这个类是JDK提供的**/
    private static final long serialVersionUID = 5516075349620653480L;
    protected transient Object  source;
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");
        this.source = source;
    }
	//这个方法就是上面Spring Events标题里说的getSource()方法
    public Object getSource() {
        return source;
    }
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}

SpringBoot启动事件的应用

根据上面说的,SpringBoot的上下文事件有四个,这四个的事件源自然都是ApplicationContext类以及其子类。为了说明SpringBoot事件是如何起作用的,将会写个例子给SpringBoot添加一个新的启动事件。在扩展之前我们先看下是怎么用的,以ContextStartedEvent为例。首先我们要添加一个监听器SpringStartListener实现ApplicationListener<ContextStartedEvent>,加上应该监听的泛型ContextStartedEvent。如果一切正常,当调用到Spring的start()方法的时候,实现方法就应该进行执行。

SpringStartListener类:

@Repository
public class SpringStartListener implements ApplicationListener<ContextStartedEvent> {
    @Override
    public void onApplicationEvent(ContextStartedEvent event) {
        System.out.println("My listener"); //事件的处理逻辑
    }
}

测试类:

public class SpringEventTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext anno=new AnnotationConfigApplicationContext(AppConfig.class);
        anno.start(); //此时触发ContextStartedEvent事件
    }
}
输出结果:
My listener

那么基于上面的结果,可以得到这样一个结论。SpringBoot在启动的时候会一直监听start(),一旦发现运行就立刻通知监听器执行应该执行的动作,这个事件就叫做ContextStartedEvent。当外部添加一个Listener监听start()方法的时候,start()就被两个监听器监听了,然后同时执行Spring的ContextStartedEvent事件和我们扩展的ContextStartedEvent事件。

SpringBoot启动事件的扩展

既然已经分析出SpringBoot的事件触发机制,那么就可以在这个基础上扩展一个事件出来。那么就添加第五个事件ContextMyEvent,这个事件类当然也必须继承自ApplicationEvent。做了事件以后就要添加一个监听器SpringMyListener,实现ApplicationListener然后在泛型里配上我们自己写的事件类ContextMyEvent。完成这些以后,只需要最后一步:触发。这个触发的步骤需要用到ApplicationContext.publishEvent()对我们生成的事件进行发布。为了发布这个事件,还需要添加一个类EventTrigger操作ApplicationContext

ContextMyEvent:

public class ContextMyEvent extends ApplicationEvent {
    private String content; //设置内容
    public ContextMyEvent(Object source) {  //设置数据源
        super(source);
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
}

SpringMyListener:

@Repository
public class SpringMyListener implements ApplicationListener<ContextMyEvent> {
    @Override
    public void onApplicationEvent(ContextMyEvent event) {
        //做一个简单的事件处理逻辑,区分content输出
        if (event.getContent().equals("name"))
            System.out.println("SpringMyListener --- fifth listener");
        else
            System.out.println("SpringMyListener --- fifth listener without name");
    }
}

EventTrigger:

@Repository
public class EventTrigger {
    ApplicationContext applicationContext;
    @Autowired
    public EventTrigger(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    //当调用的needContent()方法的时候会发布一个ContextMyEvent事件
    public void needContent(){
        //发布一个事件,并且传递事件源
        ContextMyEvent event= new ContextMyEvent(applicationContext);
        event.setContent("name"); //设置内容
        applicationContext.publishEvent(event); //发布事件
    }
}

测试类:

public class SpringEventTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext anno=new AnnotationConfigApplicationContext(AppConfig.class);
        anno.getBean(EventTrigger.class).needContent(); //设置ContextMyEvent到Spring容器中
        anno.start();
    }
}
打印输出:
SpringMyListener --- fifth listener

总结

通过上面的讲解,我们知道SpringBoot针对SpringMVC的一系列启动方法进行监听,并且根据不同的方法的启动做不同的事件处理。比如当refresh()方法调用时,SpringBoot就会做各种的配置,这一系列的行为就是所谓的SpringBoot自动配置。那么其实还留了一个小问题:我们自己写的观察者事件模型总有一个Listener注入的过程,但是Spring的例子里似乎哪里都没有这个添加的过程,这是为什么呢?

其实SpringBoot的过程里也是有的只是Spring做个更加复杂的封装。其内部针对不同的事件做了一个事件委派器ApplicationEventMulticaster,用一张图来说就是下面的模型。至于SpringBoot源码里面到底是如何实施的观察者事件模型,笔者会在下一篇开始进行讲解。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值