文章目录
本文将会通过解读SpringBoot的源码,从中学习如何使用springboot提供的事件机制,并了解其原理。最后会有示例代码,实例代码中会展示springboot的从启动到关闭的生命周期的事件监听。
1. 接口类ApplicationListener.class
在开始解读源码之前,还是先认识一些这个接口,此即可是spring中定义的监听事件的回调接口,接口中只有一个方法需要实现。所有基于spring事件的功能,都需要实现该接口,并且将实现类配置打环境中。接口源码如下:
package org.springframework.context;
import java.util.EventListener;
/**
* Interface to be implemented by application event listeners.
* Based on the standard {@code java.util.EventListener} interface
* for the Observer design pattern.
*
* <p>As of Spring 3.0, an ApplicationListener can generically declare the event type
* that it is interested in. When registered with a Spring ApplicationContext, events
* will be filtered accordingly, with the listener getting invoked for matching event
* objects only.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @param <E> the specific ApplicationEvent subclass to listen to
* @see org.springframework.context.event.ApplicationEventMulticaster
*/
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
1.1. 接口如何使用
- ApplicationListener继承了 java.util.EventListener,这没什么可说的,就是一个标记型接口而已。
- 此接口有个泛型
<E extends ApplicationEvent>
,这个泛型对象必须是事件对象ApplicationEvent或这ApplicationEvent的子类。可以看出来,当实现ApplicationListener的实现类时,是可以通过泛型来指定监听哪些类型的事件的,如果需要监听全部事件,泛型可以就简单粗暴的写<ApplicationEvent>
。 - 所以可以根据需要,自定义一个ApplicationEvent的子类。
- 需要实现方法
void onApplicationEvent(E event);
参数中会得到一个泛型指定的事件对象。
1.2. 自定义事件对象
有了监听这接口,当然还需要事件对象,事件对象必须继承ApplicationEvent。
ApplicationEvent是一个抽象类,源码如下:
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context;
import java.util.EventObject;
/**
* Class to be extended by all application events. Abstract as it
* doesn't make sense for generic events to be published directly.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public abstract class ApplicationEvent extends EventObject {
/** use serialVersionUID from Spring 1.2 for interoperability */
private static final long serialVersionUID = 7099057708183571937L;
/** System time when the event happened */
private final long timestamp;
/**
* Create a new ApplicationEvent.
* @param source the object on which the event initially occurred (never {@code null})
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* Return the system time in milliseconds when the event happened.
*/
public final long getTimestamp() {
return this.timestamp;
}
}
ApplicationEvent继承了java.util.EventObject,也就是说它只是在EventObject上添加了其他功能或者属性而已。
从源码中可以看出,事件对象仅添加了一个timestamp的属性,和获取timestamp的方法。这不难理解,就是为事件对象赋予了事件的维度了,非常的简单。
2. 源码解读
开始读源码了,读源码的思路是:
2.1. 实例化ApplicationListener的子类
这就开始吧,先看看监听者的实例化。在SpringApplication时,即run方法执行之前,Springboot实例化了配置在META-INF/spring.factories文件下配置好的实体类。看源码:
public class SpringApplication {
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
在此处实例化了ApplicationListener的子类,并保存起来
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
}
// 如此看来ApplicationListener被实例化了以后,赋值给了SpringApplication对象中的成员变量listeners
// 而且是一个ArrayList集合,如此看来,可以有多个ApplicationListener的实体类,并且时有顺序的
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>();
this.listeners.addAll(listeners);
}
在SpringApplication的构造方法中,对ApplicationListener的子类进行了实例化,但是这里实例化的仅是``文件中配置的子类。
(关于SpringApplication实例化的详细在另一文章中有描述,其中有关于读取META-INF/spring.factories文件进行实例化的源码解读,在此就不多做说明 链接)
下图是截取了部分META-INF/spring.factories文件的内容:
2.1.1. 小结
那么,可以得出了以下结论:
- 自定义的ApplicationListener子类,可以通过添加spring.factories文件到META-INF文件夹中(META-INF文件夹在src/main/resources下新建),并加入类似这样的配置
org.springframework.context.ApplicationListener=com.jwb.demo.listener.MyApplicationEventListener
即可。 - 子类必须是全限类名。
- 可以配置多个子类,每个子类之间用英文的逗号分隔","。
那么除了这种方式可以配置以外,还有其他的方式吗?请继续看下文。
2.2. 有个特殊的ApplicationListener的子类——DelegatingApplicationListener
DelegatingApplicationListener是SpringBoot定义在META-INF/spring.factories文件中ApplicationListener的实现类之一,该类的作用是用于实例化我们定义在application.properties文件中的监听器,并将这些监听器管理起来。
那么,我们需要弄明白两件事情:
- 一、DelegatingApplicationListener在什么时候监听了什么事件。
- 二、application.properties中配置监听器的key是什么。
下面先来看看DelegatingApplicationListener在监听什么事件:
public class DelegatingApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {
private static final String PROPERTY_NAME = "context.listener.classes";
private int order = 0;
private SimpleApplicationEventMulticaster multicaster;
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
// 获取applicatio.properties中配置的ApplicationListener的子类
List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
if (delegates.isEmpty()) {
return; // 如果没有配置
}
// 如果配置了自定义监听器
// 这是一个用于广播事件的对象
this.multicaster = new SimpleApplicationEventMulticaster();
// 将所有的监听器放入广播对象中进行管理
for (ApplicationListener<ApplicationEvent> listener : delegates) {
this.multicaster.addApplicationListener(listener);
}
}
if (this.multicaster != null) {
// 将当前的事件,广播出去
this.multicaster.multicastEvent(event);
}
}
@SuppressWarnings("unchecked")
private List<ApplicationListener<ApplicationEvent>> getListeners(
ConfigurableEnvironment environment) {
if (environment == null) {
return Collections.emptyList();
}
// PROPERTY_NAME 的值是 context.listener.classes
String classNames = environment.getProperty(PROPERTY_NAME);
List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<>();
if (StringUtils.hasLength(classNames)) {
for (String className : StringUtils.commaDelimitedListToSet(classNames)) {
try {
Class<?> clazz = ClassUtils.forName(className,
ClassUtils.getDefaultClassLoader());
Assert.isAssignable(ApplicationListener.class, clazz, "class ["
+ className + "] must implement ApplicationListener");
listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils
.instantiateClass(clazz));
}
catch (Exception ex) {
throw new ApplicationContextException(
"Failed to load context listener class [" + className + "]",
ex);
}
}
}
AnnotationAwareOrderComparator.sort(listeners);
return listeners;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
}
源码分析:
- 一、DelegatingApplicationListener监听了来自spring框架中的所有事件,因为接口的泛型是
<ApplicationEvent>
,但是代码中只有当event事件是ApplicationEnvironmentPreparedEvent类型的时候,才有代码逻辑可执行,ApplicationEnvironmentPreparedEvent是springboot启动时环境变量初始化完成以后产生的(这是springboot生命周期中的一部分,具体可以看关于生命周期的文章,链接)。 - 二、看看代码中执行了什么。
- 实例化了applicatio.properties中配置的ApplicationListener的子类。
- 将所有的监听器放入广播对象中进行管理。
- 将当前的事件(ApplicationEnvironmentPreparedEvent事件),广播发给刚刚实例化后的子类。
2.2.1. 小结:
从以上的代码解读中,可得出以下结论:
- 可以通过配置application.propertie文件将ApplicationListener的实现类加入环境中,可以是
context.listener.classes
。 - 自定义一个监听ApplicationEnvironmentPreparedEvent事件的实现类并配置,可以监听spring启动后环境变量初始化完成的事件。
2.3. 发布事件
最后,使用spring提供的发布事件的方法,将事件发布出去:
// 此处省略了获取context的方式,方法有多种,就不再此详细说了。
ApplicationContext context;
context.publishEvent(event);
3. 监听springboot生命周期
先创建一个ApplicationListener的实现类,如下:
package com.jwb.demo.listener;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
public class MyApplicationEventListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 在这里可以监听到Spring Boot的生命周期
if (event instanceof ApplicationEnvironmentPreparedEvent) { // 初始化环境变量
System.out.println("Spring Boot的生命周期:初始化环境变量");
} else if (event instanceof ApplicationPreparedEvent) { // 初始化完成
System.out.println("Spring Boot的生命周期:初始化完成");
} else if (event instanceof ContextRefreshedEvent) { // 应用刷新
System.out.println("Spring Boot的生命周期:应用刷新");
} else if (event instanceof ApplicationReadyEvent) {// 应用已启动完成
System.out.println("Spring Boot的生命周期:应用已启动完成");
} else if (event instanceof ContextStartedEvent) { // 应用启动,需要在代码动态添加监听器才可捕获
System.out.println("Spring Boot的生命周期:代码动态添加监听器");
} else if (event instanceof ContextStoppedEvent) { // 应用停止
System.out.println("Spring Boot的生命周期:应用停止");
} else if (event instanceof ContextClosedEvent) { // 应用关闭
System.out.println("Spring Boot的生命周期:应用关闭");
} else {
System.out.println("Spring Boot的生命周期:其他事件");
}
}
}
在application.properties配置中加入该实现类的配置
context.listener.classes=com.jwb.demo.listener.MyApplicationEventListener