SpringBoot-SpringBoot中的事件机制

在 SpringBoot 的启动过程中,会通过 SPI 机制去加载 spring.factories 下面的一些类,这里面就包括了事件相关的类。

SpringApplicationRunListener

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=org.springframework.boot.context.event.EventPublishingRunListener

ApplicationListener

# Application Listeners
org.springframework.context.ApplicationListener=org.springframework.boot.ClearCachesApplicationListener,org.springframework.boot.builder.ParentContextCloserApplicationListener,org.springframework.boot.context.FileEncodingApplicationListener,org.springframework.boot.context.config.AnsiOutputApplicationListener,org.springframework.boot.context.config.ConfigFileApplicationListener,org.springframework.boot.context.config.DelegatingApplicationListener,org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,org.springframework.boot.context.logging.LoggingApplicationListener,org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

SpringApplicationRunListener 类是 SpringBoot 中新增的类。 SpringApplication 类 中使用它们来间接调用 ApplicationListener 。另外还有一个新增的类是 SpringApplicationRunListeners , SpringApplicationRunListeners 中包含了多个 SpringApplicationRunListener 。

SpringApplicationRunListener
SpringApplicationRunListener 接口规定了 SpringBoot 的生命周期,在各个生命周期广播相应的事件,调用实际的 ApplicationListener 类。通过对 SpringApplicationRunListener 的分析,也可以对 SpringBoot 的整个启动过程的理解会有很大帮助。

先来看下 SpringApplicationRunListener 接口的代码:

public interface SpringApplicationRunListener {
    //当run方法首次启动时立即调用。可用于非常早期的初始化。
    void starting();
    //在准备好环境后,但在创建ApplicationContext之前调用。
    void environmentPrepared(ConfigurableEnvironment environment);
    //在创建和准备好ApplicationContext之后,但在加载源之前调用。
    void contextPrepared(ConfigurableApplicationContext context);
    //在加载应用程序上下文后但刷新之前调用
    void contextLoaded(ConfigurableApplicationContext context);
    //上下文已刷新,应用程序已启动,但尚未调用commandlinerunner和applicationrunner。
    void started(ConfigurableApplicationContext context);
    //在运行方法完成之前立即调用,此时应用程序上下文已刷新,
    //并且所有commandlinerunner和applicationrunner都已调用。
    //2.0 才有
    void running(ConfigurableApplicationContext context);
    //在运行应用程序时发生故障时调用。2.0 才有
    void failed(ConfigurableApplicationContext context, Throwable exception);
}

SpringApplicationRunListeners
上面提到, SpringApplicationRunListeners 是 SpringApplicationRunListener 的集合,里面包括了很多 SpringApplicationRunListener 实例; SpringApplication 类实际上使用的是 SpringApplicationRunListeners 类,与 SpringApplicationRunListener 生命周期相同,调用各个周期的 SpringApplicationRunListener 。然后广播相应的事件到 ApplicationListener 。
EventPublishingRunListener
EventPublishingRunListener 类是 SpringApplicationRunListener 接口的实现类 ,它具有广播事件的功能。其内部使用 ApplicationEventMulticaster 在实际刷新上下文之前发布事件。下面来看下 EventPublishingRunListener 类生命周期对应的事件。

ApplicationStartingEvent
ApplicationStartingEvent 是 SpringBoot 启动开始的时候执行的事件,在该事件中可以获取到 SpringApplication 对象,可做一些执行前的设置,对应的调用方法是 starting() 。

ApplicationEnvironmentPreparedEvent
ApplicationEnvironmentPreparedEvent 是 SpringBoot 对应 Enviroment 已经准备完毕时执行的事件,此时上下文 context 还没有创建。在该监听中获取到 ConfigurableEnvironment 后可以对配置信息做操作,例如:修改默认的配置信息,增加额外的配置信息等。对应的生命周期方法是 environmentPrepared(environment) ; SpringCloud 中,引导上下文就是在这时初始化的。

ApplicationContextInitializedEvent
当 SpringApplication 启动并且准备好 ApplicationContext ,并且在加载任何 bean 定义之前调用了 ApplicationContextInitializers 时发布的事件。对应的生命周期方法是 contextPrepared()

ApplicationPreparedEvent
ApplicationPreparedEvent 是 SpringBoot 上下文 context 创建完成是发布的事件;但此时 spring 中的 bean 还没有完全加载完成。这里可以将上下文传递出去做一些额外的操作。但是在该监听器中是无法获取自定义 bean 并进行操作的。对应的生命周期方法是 contextLoaded() 。

ApplicationStartedEvent
这个事件是在 2.0 版本才引入的;具体发布是在应用程序上下文刷新之后,调用任何 ApplicationRunner 和 CommandLineRunner 运行程序之前。

ApplicationReadyEvent
这个和 ApplicationStartedEvent 很类似,也是在应用程序上下文刷新之后之后调用,区别在于此时 ApplicationRunner 和 CommandLineRunner 已经完成调用了,也意味着 SpringBoot 加载已经完成。

ApplicationFailedEvent
SpringBoot 启动异常时执行的事件,在异常发生时,最好是添加虚拟机对应的钩子进行资源的回收与释放,能友善的处理异常信息。

demo 及各个事件的执行顺序
下面的各个事件对应的demo及打印出来的执行顺序。

GlmapperApplicationStartingEventListener

public class GlmapperApplicationStartingEventListener implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent applicationStartingEvent) {
        System.out.println("execute ApplicationStartingEvent ...");
    }
}

GlmapperApplicationEnvironmentPreparedEvent

public class GlmapperApplicationEnvironmentPreparedEvent implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) {
        System.out.println("execute ApplicationEnvironmentPreparedEvent ...");
    }
}

GlmapperApplicationContextInitializedEvent

public class GlmapperApplicationContextInitializedEvent implements ApplicationListener<ApplicationContextInitializedEvent> {
    @Override
    public void onApplicationEvent(ApplicationContextInitializedEvent applicationContextInitializedEvent) {
        System.out.println("execute applicationContextInitializedEvent ...");
    }
}

GlmapperApplicationPreparedEvent

public class GlmapperApplicationPreparedEvent implements ApplicationListener<ApplicationPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationPreparedEvent applicationPreparedEvent) {
        System.out.println("execute ApplicationPreparedEvent ...");
    }
}

GlmapperApplicationStartedEvent

public class GlmapperApplicationStartedEvent implements ApplicationListener<ApplicationStartedEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
        System.out.println("execute ApplicationStartedEvent ...");
    }
}

GlmapperApplicationReadyEvent

public class GlmapperApplicationReadyEvent implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        System.out.println("execute ApplicationReadyEvent ...");
    }
}

执行结果

SpringBoot 中的事件体系
这里围绕 SpringApplicationRunListener 这个类来说。在实现类 EventPublishingRunListener 中,事件发布有两种模式:

SimpleApplicationEventMulticaster
Context
所以 EventPublishingRunListener 不仅负责发布事件,而且在合适的时机将 SpringApplication 所获取的监听器和应用上下文作关联。

SimpleApplicationEventMulticaster
SimpleApplicationEventMulticaster 是 Spring 默认的事件广播器。来看下它是怎么工作的:

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        Executor executor = getTaskExecutor();
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

从上面的代码段可以看出,它是通过遍历注册的每个监听器,并启动来调用每个监听器的 onApplicationEvent 方法。

下面再来看下 SimpleApplicationEventMulticaster 的类集成结构:

这里的 AbstractApplicationContext
下面来聊,这个类实际上就负责了事件体系的初始化工作。

事件体系的初始化
事件体系的初始化对应在 SpringBoot 启动过程的 refreshContext 这个方法; refreshContext 具体调用 AbstractApplicationContext.refresh()方法,最后调用 initApplicationEventMulticaster() 来完成事件体系的初始化,代码如下:

用户可以为容器定义一个自定义的事件广播器,只要实现 ApplicationEventMulticaster 就可以了, Spring 会通过 反射的机制将其注册成容器的事件广播器,如果没有找到配置的外部事件广播器, Spring 就是默认使用 SimpleApplicationEventMulticaster 作为事件广播器。

事件注册
事件注册是在事件体系初始化完成之后做的事情,也是在 AbstractApplicationContext.refresh() 方法中进行调用的。

这里干了三件事:

首先注册静态指定的 listeners ;这里包括我们自定义的那些监听器。
调用 DefaultListableBeanFactory 中 getBeanNamesForType 得到自定义的 ApplicationListener bean 进行事件注册。
广播早期的事件。
事件广播
事件发布伴随着 SpringBoot 启动的整个生命周期。不同阶段对应发布不同的事件,上面我们已经对各个事件进行了分析,下面就具体看下发布事件的实现:

org.springframework.context.support.AbstractApplicationContext#publishEvent

earlyApplicationEvents 中的事件是广播器未建立的时候保存通知信息,一旦容器建立完成,以后都是直接通知。

广播事件最终还是通过调用 ApplicationEventMulticaster 的 multicastEvent 来实现。而 multicastEvent 也就就是事件执行的方法。

事件执行
上面 SimpleApplicationEventMulticaster 小节已经初步介绍了 multicastEvent 这个方法。补充一点, 如果有可用的 taskExecutor 会使用并发的模式执行事件,但是实际上 SimpleApplicationEventMulticaster 并没有提供线程池实现,默认请况下是使用同步的方式执行事件( org.springframework.core.task.SyncTaskExecutor ),所以如果需要异步配置的话,需要自己去实现线程池。

SpringBoot 启动过程中的事件阶段
这里回到 SpringApplication 的 run 方法,看下 SpringBoot 在启动过程中,各个事件阶段做了哪些事情。

starting -> ApplicationStartingEvent
这里 debug 到 starting 方法,追踪到 multicastEvent ,这里 type 为 ApplicationStartingEvent ;对应的事件如下:

LoggerApplicationListener:配置日志系统。使用 logging.config 环境变量指定的配置或者缺省配置
BackgroundPreinitializer:尽早触发一些耗时的初始化任务,使用一个后台线程
DelegatingApplicationListener:监听到事件后转发给环境变量 context.listener.classes 指定的那些事件监听器
LiquibaseServiceLocatorApplicationListener:使用一个可以和 SpringBoot 可执行 jar 包配合工作的版本替换 liquibase ServiceLocator
listeners.environmentPrepared->ApplicationEnvironmentPreparedEvent

AnsiOutputApplicationListener:根据 spring.output.ansi.enabled 参数配置 AnsiOutput

ConfigFileApplicationListener: EnvironmentPostProcessor ,从常见的那些约定的位置读取配置文件,比如从以下目录读取 application.properties , application.yml 等配置文件:

classpath:
file:.
classpath:config
file:./config/
也可以配置成从其他指定的位置读取配置文件。

ClasspathLoggingApplicationListener:对环境就绪事件 ApplicationEnvironmentPreparedEvent /应用失败事 件ApplicationFailedEvent 做出响应,往日志 DEBUG 级别输出 TCCL(thread context class loader) 的 classpath 。

FileEncodingApplicationListener:如果系统文件编码和环境变量中指定的不同则终止应用启动。具体的方法是比较系统属性 file.encoding 和环境变量 spring.mandatory-file-encoding 是否相等(大小写不敏感)。

listeners.contextPrepared->ApplicationContextInitializedEvent

相关监听器参考上面的描述。

listeners.contextLoaded->ApplicationPreparedEvent

相关监听器参考上面的描述。

refresh->ContextRefreshedEvent

ConditionEvaluationReportLoggingListener:实际上实现的是 ApplicationContextInitializer 接口,其目的是将 ConditionEvaluationReport 写入到日志,使用 DEBUG 级别输出。程序崩溃报告会触发一个消息输出,建议用户使用调试模式显示报告。它是在应用初始化时绑定一个 ConditionEvaluationReportListener 事件监听器,然后相应的事件发生时输出 ConditionEvaluationReport 报告。
ClearCachesApplicationListener:应用上下文加载完成后对缓存做清除工作,响应事件 ContextRefreshedEvent 。
SharedMetadataReaderFactoryContextInitializer: 向 context 注册了一个 BeanFactoryPostProcessor : CachingMetadataReaderFactoryPostProcessor 实例。
ResourceUrlProvider: handling mappings 处理
started->ApplicationStartedEvent

相关监听器参考上面的描述。

running->ApplicationReadyEvent

相关监听器参考上面的描述。

BackgroundPreinitializer&DelegatingApplicationListener
这两个贯穿了整个过程,这里拎出来单独解释下:

BackgroundPreinitializer:对于一些耗时的任务使用一个后台线程尽早触发它们开始执行初始化,这是 SpringBoot 的缺省行为。这些初始化动作也可以叫做预初始化。可以通过设置系统属性 spring.backgroundpreinitializer.ignore 为 true 可以禁用该机制。该机制被禁用时,相应的初始化任务会发生在前台线程。
DelegatingApplicationListener:监听应用事件,并将这些应用事件广播给环境属性 context.listener.classes 指定的那些监听器。
小结
到此, SpringBoot 中的事件相关的东西就结束了。本文从 SpringApplicationRunListener 这个类说起,接着介绍 SpringBoot 启动过程的事件以及事件的生命周期。最后介绍了 SpringBoot 中的内置的这些 监听器在启动过程中对应的各个阶段。

原文地址:http://blog.51cto.com/13981400/2339897

时间: 2024-10-11 07:25:17

SpringBoot-SpringBoot中的事件机制的相关文章

Flex中利用事件机制进行主程序与子窗体间参数传递

在开发具有子窗体,或者itemrenderer的应用时,常常涉及到子窗体向父窗体传递参数或者从itemrenderer内的控件向外部的主程序传递参数的需求.这些都可以通过事件机制这一统一方法加以解决.在我的应用中有两个需求: 1.左侧的List控件的itemrenderer中包含CheckBox控件,当其状态改变时需要同时改变主程序中的一个数组变量的内容:2.左下方的"新增届次"按钮会弹出一个窗口,窗口中输入届次信息后需要修改数据库中的表,同时表的更改结果要能够在List控件中体现出来

jQuery中的事件机制深入浅出

昨天呢,我们大家一起分享了jQuery中的样式选择器,那么今天我们就来看一下jQuery中的事件机制,其实,jQuery中的事件机制与JavaScript中的事件机制区别是不大的,只是,JavaScript中调用是原生的函数方法,而jQuery中调用的绑定的是jQuery中的对象方法,那么在昨天的第一篇中,我们已经说过了jQuery对象和DOM对象之间的转换,至于其中的转换的原理,我们就需要去分析一下jQuery中的源码了,这个我们在源码分析中再去做讨论, 首先呢,我们先来看一下,jQuery中

Spring 中的事件机制

说到事件机制,可能脑海中最先浮现的就是日常使用的各种 listener,listener去监听事件源,如果被监听的事件有变化就会通知listener,从而针对变化做相应的动作.这些listener是怎么实现的呢?说listener之前,我们先从设计模式开始讲起. 观察者模式 观察者模式一般包含以下几个对象: Subject:被观察的对象.它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify().目标类可以是接口,也可以是抽象类或具体类. ConcreteSubject:具体的

Java Swing中有关事件机制

看到过两种方式启动主窗体的代码: 方式1: java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new MainJFrame().setVisible(true); } }); 方式2 javax.swing.SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new MainJFrame().setVisible(

JS中的事件机制

1.事件的触发和传播 事件被用户输入系统触发,并按照DOM依次向上传播.这是两种行为,浏览器提供了两种方式来控制,即preventDefault(取消事件触发).stopPropagation(取消事件传播).所以jQuery对两种行为的控制是分开的,具体如下图. 2.自定义事件 JS中事件分两种类型,浏览器定义的基本事件类型,譬如click,blur,change,mouseover等,还支持用户自定义事件类型. 事件可以用户自定义,自定义事件通过绑定自定义事件到DOM元素和触发DOM上的自定

android中的事件传递和处理机制

一直以来,都被android中的事件传递和处理机制深深的困扰!今天特意来好好的探讨一下.现在的感觉是,只要你理解到位,其实事件的 传递和处理机制并没有想象中的那么难.总之,不要自己打击自己,要相信自己能掌握这块知识.好了,下面是我今天的收获,希望也 能对你有一点帮助. 一.拟人化来理解android中的事件机制 其实android中的事件传递与处理机制跟我们生活中的事件处理是一样的.这里有一个生活中的例子,很能说明这个问题.阐述如下: 你是一个公司的员工,你的上头有一个主管,主管上头呢还有一个经

Android中的事件分发机制

1. 一个小问题引发的思考 2. 通过源码探索View中的事件分发机制 3.通过源码探索ViewGroup的事件分发机制 最近的一个项目中涉及,布局为一个RelativeLayout包含了一个EditText和一个Button,当点击EditText时,弹出软键盘,点击RelativeLayout中除了EditText和Button之外其它的地方时,收起软键盘. 实现起来很简单,为EditText和RelativeLayout分别注册一个onTouch事件,为Button注册一个click事件,

观察者模式之spring事件机制

ddsspring中的事件机制使用到设计模式中的观察者模式 ,观察者模式有两个概念,1.观察者.被观察者.2.被观察者做出相应得动作,观察者能接收到.不分析设计模式,学习下spring中的事件机制实际开发如何使用 及使用场景 . spring中的事件机制涉及到者几个类文件 :ApplicationEvent(事件类型).ApplicationListener(事件监听类).ApplicationEventPublisher(事件发布类).先看看这几个类的继承.实现关系: Application类

搞清楚Spring事件机制后:Spring的源码看起来简单多了

本文主讲Spring的事件机制,意图说清楚: 什么是观察者模式? 自己实现事件驱动编程,对标Spring的事件机制 彻底搞懂Spring中的事件机制,从而让大家 本文内容较长,代码干货较多,建议收藏后持续阅读. Spring框架已然是Javaeee开发领域的霸主,无论是使用SpringBoot还是SpringCloud,都离不开Spring框架. 作为Java开发者,无论是面试求职还是日常开发,就必须得熟练掌握.运用Spring框架. 因此学习Spring框架源码也就成为了大家最重要的事情之一.