springboot提供了两个接口来实现监听:ApplicationListener、SmartApplicationListener,如下图。显而易见,SmartApplicationListener 是 ApplicationListener 的子类,故而其功能要强于 ApplicationListener。


当然,springboot很贴心地提供了一个 @EventListener 注解来实现监听。
首先,先来简单体验一下监听器的功能。
需求: 在spring容器初始化完成之后就开始监听,并打印日志。
实现:
准备springboot工程(依赖:springboot、lombok)
写一个监听器
@Slf4j @Component public class MyTask implements ApplicationListener{ private static boolean aFlag = false; @Override public void onApplicationEvent(ContextRefreshedEvent event) { if (!aFlag) { aFlag = true; log.info("我已经监听到了"); } } }
启动项目,控制台输出如下:

现在来说一下为什么要这么写监听器:
实现接口
implements ApplicationListener
自定义监听器需要实现 ApplicationListener
接口 ContextRefreshedEvent 是一个事件,它会在 spring容器初始化完成 之后被触发,所以监听器就会在 spring容器初始化完成之后开始监听,所以这就是所谓的全局监听
标志位 aFlag
private static boolean aFlag = false;
aFlag 是一个启动标志
因为web应用会出现父子容器,这样就会触发两次监听任务,所以需要一个标志位,保证监听任务(log.info(“我已经监听到了”))只会触发一次
需求: 实现一个定时任务,每间隔5秒、10秒、15秒、20秒、25秒、30秒、40秒、50秒、60秒,在控制台打印一次日志。
实现: 可通过多线程的方式实现
新建一个类 TimerRunner 继承 Runnable
5秒到60秒的间隔可以通过 枚举类实现
private enum TimerEnum {
// 第5秒打印
FIRST(1, 5 ),
// 第10秒打印
SECOND(2, 10),
// 第15秒打印
THIRD(3, 15),
// 第20秒打印
FOURTH(4, 20),
// 第25秒打印
FIFTH(5, 25),
// 第30秒打印
SIXTH(6, 30),
// 第40秒打印
SEVENTH(7, 40),
// 第50秒打印
EIGHTH(8, 50),
// 第60秒打印
NINTH(9, 60);
private Integer count;
private Integer time;
TimerEnum(Integer count, Integer time) {
this.count = count;
this.time = time;
}
public Integer getCount() {
return count;
}
public Integer getTime() {
return time;
}
}
TimeRunner 完整代码
@Slf4j
public class TimerRunner implements Runnable{
@Override
public void run() {
// 打印次数
int count = 1;
SimpleDateFormat dateFormat= new SimpleDateFormat("hh:mm:ss");
for (TimerEnum item: TimerEnum.values()) {
if (count == item.getCount()) {
if (count != 9) {
log.info("时间: " + dateFormat.format(new Date()) + "第 " + count + " 次打印,还剩余 " + (9 - count) + " 次完成打印");
count++;
} else {
log.info("最后一次打印");
log.info("已完成所有打印任务!");
}
}
try {
// TimeUnit来sleep,可读性更好
TimeUnit.SECONDS.sleep(item.getTime());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
private enum TimerEnum {
// 第5秒打印
FIRST(1, 5 ),
// 第10秒打印
SECOND(2, 10),
// 第15秒打印
THIRD(3, 15),
// 第20秒打印
FOURTH(4, 20),
// 第25秒打印
FIFTH(5, 25),
// 第30秒打印
SIXTH(6, 30),
// 第40秒打印
SEVENTH(7, 40),
// 第50秒打印
EIGHTH(8, 50),
// 第60秒打印
NINTH(9, 60);
private Integer count;
private Integer time;
TimerEnum(Integer count, Integer time) {
this.count = count;
this.time = time;
}
public Integer getCount() {
return count;
}
public Integer getTime() {
return time;
}
}
}
MyTask 代码
@Slf4j @Component public class MyTask implements ApplicationListener{ private static boolean aFlag = false; @Override public void onApplicationEvent(ContextRefreshedEvent event) { if (!aFlag) { aFlag = true; new Thread(new TimerRunner()).start(); } } }
控制台输出:

Spring的 ApplicationContext 提供了支持事件和代码中监听器的功能。
我们可以创建bean用来监听在 ApplicationContext 中发布的事件。ApplicationEvent 类在 ApplicationContext 接口中处理的事件,如果一个bean实现了 ApplicationListener 接口,当一个 ApplicationEvent 被发布以后,bean会自动被通知。
参考链接:https://cloud.tencent.com/developer/article/1532994
先来看一下 spring的内置事件 :
内置事件: 参考链接: https://blog.csdn.net/liyantianmin/article/details/81017960
| 事件 | 说明 |
|---|---|
| ContextRefreshedEvent | ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用。 |
| ContextStartedEvent | 当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。 |
| ContextStoppedEvent | 当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。 |
| ContextClosedEvent | 当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。 |
| RequestHandledEvent | 这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。 |
自定义监听事件:
extends ApplicationEvent 自定义事件
public class MyEvent extends ApplicationEvent {
private String time = new SimpleDateFormat("hh:mm:ss").format(new Date());
private String msg;
public MyEvent(Object source, String msg) {
super(source);
this.msg = msg;
}
public MyEvent(Object source) {
super(source);
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
监听器
@Slf4j
@Component
public class MyTask implements ApplicationListener {
private static boolean aFlag = false;
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
log.info("监听到 ContextRefreshedEvent...");
}
if (event instanceof MyEvent) {
log.info("监听到 MyEvent...");
MyEvent myEvent = (MyEvent) event;
System.out.println("时间:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());
}
}
}
触发事件
自定义监听事件需要主动触发
@SpringBootApplication
public class TaskApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TaskApplication.class, args);
MyEvent event = new MyEvent("event", "忙中岁月忙中遣,我本愚来性不移");
// 发布事件
run.publishEvent(event);
}
}
也可以这样触发,美观一点
@SpringBootApplication
public class TaskApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(TaskApplication.class, args);
}
@Resource
private ApplicationContext applicationContext;
@Override
public void run(String... args) throws Exception {
MyEvent event = new MyEvent("event", "忙中岁月忙中遣,我本愚来性不移");
// 发布事件
applicationContext.publishEvent(event);
}
}
控制台输出

@Slf4j
@Component
public class MyTask implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class extends ApplicationEvent> eventType) {
return eventType == MyEvent.class || eventType == ContextRefreshedEvent.class;
}
@Override
public int getOrder() {
return 0;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
log.info("监听到 ContextRefreshedEvent...");
}
if (event instanceof MyEvent) {
log.info("监听到 MyEvent...");
MyEvent myEvent = (MyEvent) event;
System.out.println("时间:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());
}
}
}
public interface SmartApplicationListener extends ApplicationListener, Ordered { boolean supportsEventType(Class extends ApplicationEvent> eventType); default boolean supportsSourceType(@Nullable Class> sourceType) { return true; } @Override default int getOrder() { return LOWEST_PRECEDENCE; } default String getListenerId() { return ""; } }
| 方法 | 说明 |
|---|---|
| supportsEventType | 确认当前监听器是否支持当前事件类型。 |
| supportsSourceType | 确定此监听器是否实际支持给定的源类型。 |
| getOrder | 确定此侦听器在同一事件的一组侦听器中的顺序。数值越小,优先级越高。 |
| getListenerId | 返回侦听器的可选标识符。 |
使用:
@Slf4j
@Component
public class MyTask {
@EventListener
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
log.info("监听到 ContextRefreshedEvent...");
}
if (event instanceof MyEvent) {
log.info("监听到 MyEvent...");
MyEvent myEvent = (MyEvent) event;
System.out.println("时间:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());
}
}
}
@Slf4j
@Component
public class MyTask {
@EventListener
public void MyEventListener(MyEvent event) {
log.info("监听到 MyEvent...");
MyEvent myEvent = (MyEvent) event;
System.out.println("时间:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());
}
@EventListener
public void ContextRefreshedEventListener(MyEvent event) {
log.info("监听到 ContextRefreshedEvent...");
}
}
指定监听事件的类型:
@EventListener(MyEvent.class)
@EventListener({MyEvent.class, ContextRefreshedEvent.class})