构造流程源码分析:ApplicationListener加载

ApplicationListener加载完成了 ApplicationContextlnitializer 的加载之后 , 便会进行 ApplicationListener 的加载 。 它的常见应用场景为:当容器初始化完成之后 , 需要处理一些如数据的加载、初始化缓存、特定任务的注册等操作 。 而在此阶段 , 更多的是用于 ApplicationContext 管理 Bean 过程的场景 。
Spring 事件传播机制是基于观察者模式(Observer) 实现的 。 比如 , 在 ApplicationContext管 理 Bean 生 命 周 期 的 过 程 中,会 将 一 些 改 变 定 义 为 事 件 ( ApplicationEvent )。
ApplicationContext 通过 ApplicationListener 监听 ApplicationEvent,当事件被发布之后 , ApplicationListener 用来对事件做出具体的操作 。
构造流程源码分析:ApplicationListener加载文章插图
ApplicationListener 的整个配置和加载流程与 ApplicationContexthnitializer 完全一致 ,也是 先 通 过SpringFactoriesLoader的loadFactoryNames方 法 获 得META-INF/spring.factories 中对应配置 , 然后再进行实例化 , 最后将获得的结果集合添加到SpringApplication 的成员变量 listeners 中 , 代码如下 。
private List> listeners;public void setl isteners(Collection<了 extends App. lcationLi stenerli steners)this. listeners = new Arraylist<>(listeners);同样的 , 在调用 setListeners 方法时也会进行覆盖赋值的操作 , 之前加载的内容会被清除 。
下 面 我 们 看 看 ApplicationListener 这里的基本使 用。ApplicationListener 接 口 和ApplicationEvent 类配合使用 , 可实现 ApplicationContext 的事件处理 。如果容器中存在AplicationListener 的 Bean , 当 ApplicationContext 调用 publishEvent 方法时 , 对应的 Bean会被触发 。 这就是上文提到的观察者模式的实现 。
在接口 ApplicationL istener 中只定义了一个 onAplicationEvent 方法 , 当监听事件被触发时 , onApplicationEvent 方法会被执行 , 接口 ApplicationListener 的源代码如下 。
@FunctionalInterfacepublic interface ApplicationListener extends EventListenervoid onApplicationEvent(E event);}onApplicationEvent 方法一般用于处理应用程序事件 , 参数 event 为 ApplicationEvent 的子类 , 是具体响应(接收到)的事件 。
当 ApplicationContext 被初始化或刷新时 , 会触发 ContextRefreshedEvent 事件 , 下面我们就实现一-个 ApplicationListener 来监听此事件的发生 , 代码如下 。
@Component //需对该类进行 Bean 的实例化public class LearnListener implements ApplicationListener@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {/打印容中出 Beon8 得空婴中初始化ystem. out. println(" 监听器获得容器+ event. getApplication-Context(). getBeanDefinitionCount());}上面的 LearnListener 实现了 ApplicationListener 并监听 ContextRefreshedEvent 事件 , 当容器创建或刷新时 , 该监听器的 onApplicationEvent 方法会被调用 , 并打印出当前容器中 Bean 的数量 。
在具体的实战业务中 , 我们也可以自定义事件 , 在完成业务之后手动触发对应的事件监听器 , 也就是手动调用 ApplicationContext 的 publishEvent(ApplicationEvent event)方法 。
构造流程源码分析:ApplicationListener加载文章插图
入口类推断创 建 SpringApplication 的 最 后 一 步 便 是 推 断 入 口 类,我 们 通 过 调 用 自 身 的deduce-MainApplicationClass 方法来进行入口类的推断 。
private Class deduceMainApplicationClass() {/获取栈元素数组StackTraceElement[] stackTrace = new Runt imeException() . getStackTrace()//遍历栈元素数组for (StackTraceElement stackTraceElement : stackTrace) {//匹配第一个 main 方法 , 并返回("main" . equals(stackTraceElement , getMethodName())) {return Class. forName( stackTraceElement . getClassName());} catch (ClassNotFoundException ex) {//如果发生异常 , 忽略该异常 , 并继续执行return null;}该方法实现的基本流程就是先创建一个运行时异常 ,然后获得栈数组 , 遍历栈数组 , 判断类的方法中是否包含 main 方法 。 第-个被匹配的类会通过 Class.forName 方法创建对象 , 并 将 其 被 返 回,最 后 在 上 层 方 法 中 将 对 象 赋 值 给 SpringApplication 的 成 员 变 量mainApplicationClass 。 在遍历过程中如果发生异常 , 会忽略掉该异常并继续执行遍历操作 。
至此 , 整个 SpringApplication 类的实例化过程便完成 了 。
构造流程源码分析:ApplicationListener加载文章插图
SpringApplication的定制化配置前面我们学习了 Spring Boot 启动过程中构建 SpringApplication 所做的一系列初始化操作 , 这些操作都是 Spring Boot 默认配置的 。 如果在此过程中需要定制化配置 , Spring Boot 在SpringApplication 类中也提供了相应的入口 。