我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复【资料】,即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板。


Spring 提供了 ApplicationContext 事件机制,可以发布和监听事件,这个特性非常有用。

Spring 内置了一些事件和监听器,例如在 Spring 容器启动前,Spring 容器启动后,应用启动失败后等事件发生后,监听在这些事件上的监听器会做出相应的响应处理。

当然,我们也可以自定义监听器,监听 Spring 原有的事件。或者自定义我们自己的事件和监听器,在必要的时间点发布事件,然后监听器监听到事件就做出响应处理。


ApplicationContext 事件机制采用观察者设计模式来实现,通过 ApplicationEvent 事件类和 ApplicationListener 监听器接口,可以实现 ApplicationContext 事件发布与处理。

每当 ApplicationContext 发布 ApplicationEvent 时,如果 Spring 容器中有 ApplicationListener bean,则监听器会被触发执行相应的处理。当然,ApplicationEvent 事件的发布需要显示触发,要么 Spring 显示触发,要么我们显示触发。


定义应用监听器需要实现的接口。此接口继承了 JDK 标准的事件监听器接口 EventListener,EventListener 接口是一个空的标记接口,推荐所有事件监听器必须要继承它。

  1. package org.springframework.context;
  2. import java.util.EventListener;
  3. @FunctionalInterface
  4. public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
  5. /**
  6. * 处理应用事件
  7. */
  8. void onApplicationEvent(E event);
  9. }
  1. package java.util;
  2. public interface EventListener {
  3. }

ApplicationListener 是个泛型接口,我们自定义此接口的实现类时,如果指定了泛型的具体事件类,那么只会监听此事件。如果不指定具体的泛型,则会监听 ApplicationEvent 抽象类的所有子类事件。

如下我们定义一个监听器,监听具体的事件,例如监听 ApplicationStartedEvent 事件。

  1. package com.chenpi;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.boot.context.event.ApplicationStartedEvent;
  4. import org.springframework.context.ApplicationListener;
  5. import org.springframework.stereotype.Component;
  6. /**
  7. * @Description
  8. * @Author 陈皮
  9. * @Date 2021/6/26
  10. * @Version 1.0
  11. */
  12. @Slf4j
  13. @Component
  14. public class MyApplicationListener implements ApplicationListener<ApplicationStartedEvent> {
  15. @Override
  16. public void onApplicationEvent(ApplicationStartedEvent event) {
  17. log.info(">>> MyApplicationListener:{}", event);
  18. }
  19. }

启动服务,会发现在服务启动后,此监听器被触发了。

在这里插入图片描述

如果不指定具体的泛型类,则会监听 ApplicationEvent 抽象类的所有子类事件。如下所示:

  1. package com.chenpi;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.context.ApplicationEvent;
  4. import org.springframework.context.ApplicationListener;
  5. import org.springframework.stereotype.Component;
  6. /**
  7. * @Description
  8. * @Author 陈皮
  9. * @Date 2021/6/26
  10. * @Version 1.0
  11. */
  12. @Slf4j
  13. @Component
  14. public class MyApplicationListener implements ApplicationListener {
  15. @Override
  16. public void onApplicationEvent(ApplicationEvent event) {
  17. log.info(">>> MyApplicationListener:{}", event);
  18. }
  19. }

在这里插入图片描述

注意,监听器类的 bean 要注入到 Spring 容器中,不然不会生效。一种是使用注解注入,例如 @Component。另外可以使用 SpringApplicationBuilder.listeners() 方法添加,不过这两种方式有区别的,看以下示例。

首先我们使用 @Component 注解方式,服务启动时,监视到了2个事件:

  • ApplicationStartedEvent
  • ApplicationReadyEvent
  1. package com.chenpi;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.boot.context.event.SpringApplicationEvent;
  4. import org.springframework.context.ApplicationListener;
  5. import org.springframework.stereotype.Component;
  6. /**
  7. * @Description
  8. * @Author 陈皮
  9. * @Date 2021/6/26
  10. * @Version 1.0
  11. */
  12. @Slf4j
  13. @Component
  14. public class MyApplicationListener implements ApplicationListener<SpringApplicationEvent> {
  15. @Override
  16. public void onApplicationEvent(SpringApplicationEvent event) {
  17. log.info(">>> MyApplicationListener:{}", event);
  18. }
  19. }

在这里插入图片描述

而使用 SpringApplicationBuilder.listeners() 方法添加监听器,服务启动时,监听到了5个事件:

  • ApplicationEnvironmentPreparedEvent
  • ApplicationContextInitializedEvent
  • ApplicationPreparedEvent
  • ApplicationStartedEvent
  • ApplicationReadyEvent
  1. package com.chenpi;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.builder.SpringApplicationBuilder;
  5. import org.springframework.context.ConfigurableApplicationContext;
  6. @SpringBootApplication
  7. public class Application {
  8. public static void main(String[] args) {
  9. // SpringApplication.run(Application.class, args);
  10. SpringApplication app = new SpringApplicationBuilder(Application.class)
  11. .listeners(new MyApplicationListener()).build();
  12. app.run(args);
  13. }
  14. }

在这里插入图片描述

其实这是和监听器 bean 注册的时机有关,@component 注解的监听器 bean 只有在 bean 初始化注册完后才能使用;而通过 SpringApplicationBuilder.listeners() 添加的监听器 bean 是在容器启动前,所以监听到的事件比较多。但是注意,这两个不要同时使用,不然监听器会重复执行两遍。

如果你想在监听器 bean 中注入其他 bean(例如 @Autowired),那最好是使用注解形式,因为如果太早发布监听器,可能其他 bean 还未初始化完成,可能会报错。


ApplicationEvent 是所有应用事件需要继承的抽象类。它继承了 EventObject 类,EventObject 是所有事件的根类,这个类有个 Object 类型的对象 source,代表事件源。所有继承它的类的构造函数都必须要显示传递这个事件源。

  1. package org.springframework.context;
  2. import java.util.EventObject;
  3. public abstract class ApplicationEvent extends EventObject {
  4. private static final long serialVersionUID = 7099057708183571937L;
  5. // 发布事件的系统时间
  6. private final long timestamp;
  7. public ApplicationEvent(Object source) {
  8. super(source);
  9. this.timestamp = System.currentTimeMillis();
  10. }
  11. public final long getTimestamp() {
  12. return this.timestamp;
  13. }
  14. }
  1. package java.util;
  2. public class EventObject implements java.io.Serializable {
  3. private static final long serialVersionUID = 5516075349620653480L;
  4. protected transient Object source;
  5. public EventObject(Object source) {
  6. if (source == null)
  7. throw new IllegalArgumentException("null source");
  8. this.source = source;
  9. }
  10. public Object getSource() {
  11. return source;
  12. }
  13. public String toString() {
  14. return getClass().getName() + "[source=" + source + "]";
  15. }
  16. }

在 Spring 中,比较重要的事件类是 SpringApplicationEvent。Spring 有一些内置的事件,当完成某种操作时会触发某些事件。这些内置事件继承 SpringApplicationEvent 抽象类。SpringApplicationEvent 继承 ApplicationEvent 并增加了字符串数组参数字段 args。

  1. /**
  2. * Base class for {@link ApplicationEvent} related to a {@link SpringApplication}.
  3. *
  4. * @author Phillip Webb
  5. * @since 1.0.0
  6. */
  7. @SuppressWarnings("serial")
  8. public abstract class SpringApplicationEvent extends ApplicationEvent {
  9. private final String[] args;
  10. public SpringApplicationEvent(SpringApplication application, String[] args) {
  11. super(application);
  12. this.args = args;
  13. }
  14. public SpringApplication getSpringApplication() {
  15. return (SpringApplication) getSource();
  16. }
  17. public final String[] getArgs() {
  18. return this.args;
  19. }
  20. }

在这里插入图片描述

我们可以编写自己的监听器,然后监听这些事件,实现自己的业务逻辑。例如编写 ApplicationListener 接口的实现类,监听 ContextStartedEvent 事件,当应用容器 ApplicationContext 启动时,会发布该事件,所以我们编写的监听器会被触发。

  • ContextRefreshedEvent:ApplicationContext 被初始化或刷新时,事件被发布。ConfigurableApplicationContext接口中的 refresh() 方法被调用也会触发事件发布。初始化是指所有的 Bean 被成功装载,后处理 Bean 被检测并激活,所有单例 Bean 被预实例化,ApplicationContext 容器已就绪可用。
  • ContextStartedEvent:应用程序上下文被刷新后,但在任何 ApplicationRunner 和 CommandLineRunner 被调用之前,发布此事件。
  • ApplicationReadyEvent:此事件会尽可能晚地被发布,以表明应用程序已准备好为请求提供服务。事件源是SpringApplication 本身,但是要注意修改它的内部状态,因为到那时所有初始化步骤都已经完成了。
  • ContextStoppedEvent:ConfigurableApplicationContext 接口的 stop() 被调用停止 ApplicationContext 时,事件被发布。
  • ContextClosedEvent:ConfigurableApplicationContext 接口的 close() 被调用关闭 ApplicationContext 时,事件被发布。注意,一个已关闭的上下文到达生命周期末端后,它不能被刷新或重启。
  • ApplicationFailedEvent:当应用启动失败后发布事件。
  • ApplicationEnvironmentPreparedEvent:事件是在 SpringApplication 启动时发布的,并且首次检查和修改 Environment 时,此时上 ApplicationContext 还没有创建。
  • ApplicationPreparedEvent:事件发布时,SpringApplication 正在启动,ApplicationContext 已经完全准备好,但没有刷新。在这个阶段,将加载 bean definitions 并准备使用 Environment。
  • RequestHandledEvent:这是一个 web 事件,只能应用于使用 DispatcherServlet 的 Web 应用。在使用 Spring 作为前端的 MVC 控制器时,当 Spring 处理用户请求结束后,系统会自动触发该事件。


前面介绍了自定义监听器,然后监听 Spring 原有的事件。下面介绍自定义事件和自定义监听器,然后在程序中发布事件,触发监听器执行,实现自己的业务逻辑。

首先自定义事件,继承 ApplicationEvent,当然事件可以自定义自己的属性。

  1. package com.chenpi;
  2. import lombok.Getter;
  3. import lombok.Setter;
  4. import lombok.ToString;
  5. import org.springframework.context.ApplicationEvent;
  6. /**
  7. * @Description 自定义事件
  8. * @Author 陈皮
  9. * @Date 2021/6/26
  10. * @Version 1.0
  11. */
  12. @Getter
  13. @Setter
  14. public class MyApplicationEvent extends ApplicationEvent {
  15. // 事件可以增加自己的属性
  16. private String myField;
  17. public MyApplicationEvent(Object source, String myField) {
  18. // 绑定事件源
  19. super(source);
  20. this.myField = myField;
  21. }
  22. @Override
  23. public String toString() {
  24. return "MyApplicationEvent{" + "myField='" + myField + '\'' + ", source=" + source + '}';
  25. }
  26. }

然后自定义监听器,监听我们自定义的事件。

  1. package com.chenpi;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.context.ApplicationListener;
  4. /**
  5. * @Description 自定义监听器
  6. * @Author 陈皮
  7. * @Date 2021/6/26
  8. * @Version 1.0
  9. */
  10. @Slf4j
  11. public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
  12. @Override
  13. public void onApplicationEvent(MyApplicationEvent event) {
  14. log.info(">>> MyApplicationListener:{}", event);
  15. }
  16. }

注册监听器和发布事件。注册监听器上面讲解了有两种方式。事件的发布可以通过 ApplicationEventPublisher.publishEvent() 方法。此处演示直接用 configurableApplicationContext 发布,它实现了 ApplicationEventPublisher 接口。

  1. package com.chenpi;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.builder.SpringApplicationBuilder;
  5. import org.springframework.context.ConfigurableApplicationContext;
  6. @SpringBootApplication
  7. public class Application {
  8. public static void main(String[] args) {
  9. // SpringApplication.run(Application.class, args);
  10. // 注册监听器
  11. SpringApplication app = new SpringApplicationBuilder(Application.class)
  12. .listeners(new MyApplicationListener()).build();
  13. ConfigurableApplicationContext configurableApplicationContext = app.run(args);
  14. // 方便演示,在项目启动后发布事件,当然也可以在其他操作和其他时间点发布事件
  15. configurableApplicationContext
  16. .publishEvent(new MyApplicationEvent("我是事件源,项目启动成功后发布事件", "我是自定义事件属性"));
  17. }
  18. }

启动服务,结果显示确实监听到发布的事件了。

  1. 2021-06-26 16:15:09.584 INFO 10992 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8081 (http) with context path ''
  2. 2021-06-26 16:15:09.601 INFO 10992 --- [ main] com.chenpi.Application : Started Application in 2.563 seconds (JVM running for 4.012)
  3. 2021-06-26 16:15:09.606 INFO 10992 --- [ main] com.chenpi.MyApplicationListener : >>> MyApplicationListenerMyApplicationEvent{myField='我是自定义事件属性', source=我是事件源,项目启动成功后发布事件}

事件监听机制能达到分发,解耦效果。例如可以在业务类中发布事件,让监听在此事件的监听器执行自己的业务处理。例如:

  1. package com.chenpi;
  2. import org.springframework.context.ApplicationEventPublisher;
  3. import org.springframework.context.ApplicationEventPublisherAware;
  4. import org.springframework.stereotype.Service;
  5. /**
  6. * @Description
  7. * @Author 陈皮
  8. * @Date 2021/6/26
  9. * @Version 1.0
  10. */
  11. @Service
  12. public class MyService implements ApplicationEventPublisherAware {
  13. private ApplicationEventPublisher applicationEventPublisher;
  14. @Override
  15. public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
  16. this.applicationEventPublisher = applicationEventPublisher;
  17. }
  18. public void testEvent() {
  19. applicationEventPublisher
  20. .publishEvent(new MyApplicationEvent("我是事件源", "我是自定义事件属性"));
  21. }
  22. }


除了实现 ApplicationListener 接口创建监听器外,Spring 还提供了注解 @EventListener 来创建监听器。

  1. package com.chenpi;
  2. import org.springframework.context.event.EventListener;
  3. import org.springframework.stereotype.Component;
  4. import lombok.extern.slf4j.Slf4j;
  5. /**
  6. * @Description 自定义监听器
  7. * @Author 陈皮
  8. * @Date 2021/6/26
  9. * @Version 1.0
  10. */
  11. @Slf4j
  12. @Component
  13. public class MyApplicationListener01 {
  14. @EventListener
  15. public void onApplicationEvent(MyApplicationEvent event) {
  16. log.info(">>> MyApplicationListener:{}", event);
  17. }
  18. }

而且注解还可以通过条件过滤只监听指定条件的事件。例如事件的 myField 属性的值等于”陈皮”的事件。

  1. package com.chenpi;
  2. import org.springframework.context.event.EventListener;
  3. import org.springframework.stereotype.Component;
  4. import lombok.extern.slf4j.Slf4j;
  5. /**
  6. * @Description 自定义监听器
  7. * @Author 陈皮
  8. * @Date 2021/6/26
  9. * @Version 1.0
  10. */
  11. @Slf4j
  12. @Component
  13. public class MyApplicationListener01 {
  14. @EventListener(condition = "#event.myField.equals('陈皮')")
  15. public void onApplicationEvent(MyApplicationEvent event) {
  16. log.info(">>> MyApplicationListener:{}", event);
  17. }
  18. }

还可以在同一个类中定义多个监听,对同一个事件的不同监听还可以指定顺序。order 值越小越先执行。

  1. package com.chenpi;
  2. import org.springframework.context.event.EventListener;
  3. import org.springframework.core.annotation.Order;
  4. import org.springframework.stereotype.Component;
  5. import lombok.extern.slf4j.Slf4j;
  6. /**
  7. * @Description 自定义监听器
  8. * @Author 陈皮
  9. * @Date 2021/6/26
  10. * @Version 1.0
  11. */
  12. @Slf4j
  13. @Component
  14. public class MyApplicationListener01 {
  15. @Order(2)
  16. @EventListener
  17. public void onApplicationEvent(MyApplicationEvent event) {
  18. log.info(">>> onApplicationEvent order=2:{}", event);
  19. }
  20. @Order(1)
  21. @EventListener
  22. public void onApplicationEvent01(MyApplicationEvent event) {
  23. log.info(">>> onApplicationEvent order=1:{}", event);
  24. }
  25. @EventListener
  26. public void otherEvent(YourApplicationEvent event) {
  27. log.info(">>> otherEvent:{}", event);
  28. }
  29. }

执行结果如下:

  1. >>> onApplicationEvent order=1MyApplicationEvent{myField='陈皮', source=我是事件源}
  2. >>> onApplicationEvent order=2MyApplicationEvent{myField='陈皮', source=我是事件源}
  3. >>> otherEventMyApplicationEvent{myField='我是自定义事件属性01', source=我是事件源01}

事件的监听处理是同步的,如下:

  1. package com.chenpi;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.context.ApplicationEventPublisher;
  4. import org.springframework.context.ApplicationEventPublisherAware;
  5. import org.springframework.stereotype.Service;
  6. /**
  7. * @Description
  8. * @Author 陈皮
  9. * @Date 2021/6/26
  10. * @Version 1.0
  11. */
  12. @Service
  13. @Slf4j
  14. public class MyService implements ApplicationEventPublisherAware {
  15. private ApplicationEventPublisher applicationEventPublisher;
  16. @Override
  17. public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
  18. this.applicationEventPublisher = applicationEventPublisher;
  19. }
  20. public void testEvent() {
  21. log.info(">>> testEvent begin");
  22. applicationEventPublisher.publishEvent(new MyApplicationEvent("我是事件源", "陈皮"));
  23. applicationEventPublisher.publishEvent(new YourApplicationEvent("我是事件源01", "我是自定义事件属性01"));
  24. log.info(">>> testEvent end");
  25. }
  26. }

执行结果如下:

  1. 2021-06-26 20:34:27.990 INFO 12936 --- [nio-8081-exec-1] com.chenpi.MyService : >>> testEvent begin
  2. 2021-06-26 20:34:27.990 INFO 12936 --- [nio-8081-exec-1] com.chenpi.MyApplicationListener01 : >>> onApplicationEvent order=1MyApplicationEvent{myField='陈皮', source=我是事件源}
  3. 2021-06-26 20:34:27.991 INFO 12936 --- [nio-8081-exec-1] com.chenpi.MyApplicationListener01 : >>> onApplicationEvent order=2MyApplicationEvent{myField='陈皮', source=我是事件源}
  4. 2021-06-26 20:34:27.992 INFO 12936 --- [nio-8081-exec-1] com.chenpi.MyApplicationListener01 : >>> otherEventMyApplicationEvent{myField='我是自定义事件属性01', source=我是事件源01}
  5. 2021-06-26 20:34:27.992 INFO 12936 --- [nio-8081-exec-1] com.chenpi.MyService : >>> testEvent end

不过,我们也可以显示指定异步方式去执行监听器,记得在服务添加 @EnableAsync 注解开启异步注解。

  1. package com.chenpi;
  2. import org.springframework.context.event.EventListener;
  3. import org.springframework.core.annotation.Order;
  4. import org.springframework.scheduling.annotation.Async;
  5. import org.springframework.stereotype.Component;
  6. import lombok.extern.slf4j.Slf4j;
  7. /**
  8. * @Description 自定义监听器
  9. * @Author 陈皮
  10. * @Date 2021/6/26
  11. * @Version 1.0
  12. */
  13. @Slf4j
  14. @Component
  15. public class MyApplicationListener01 {
  16. @Async
  17. @Order(2)
  18. @EventListener
  19. public void onApplicationEvent(MyApplicationEvent event) {
  20. log.info(">>> onApplicationEvent order=2:{}", event);
  21. }
  22. @Order(1)
  23. @EventListener
  24. public void onApplicationEvent01(MyApplicationEvent event) {
  25. log.info(">>> onApplicationEvent order=1:{}", event);
  26. }
  27. @Async
  28. @EventListener
  29. public void otherEvent(YourApplicationEvent event) {
  30. log.info(">>> otherEvent:{}", event);
  31. }
  32. }

执行结果如下,注意打印的线程名。

  1. 2021-06-26 20:37:04.807 INFO 9092 --- [nio-8081-exec-1] com.chenpi.MyService : >>> testEvent begin
  2. 2021-06-26 20:37:04.819 INFO 9092 --- [nio-8081-exec-1] com.chenpi.MyApplicationListener01 : >>> onApplicationEvent order=1MyApplicationEvent{myField='陈皮', source=我是事件源}
  3. 2021-06-26 20:37:04.831 INFO 9092 --- [ task-1] com.chenpi.MyApplicationListener01 : >>> onApplicationEvent order=2MyApplicationEvent{myField='陈皮', source=我是事件源}
  4. 2021-06-26 20:37:04.831 INFO 9092 --- [nio-8081-exec-1] com.chenpi.MyService : >>> testEvent end
  5. 2021-06-26 20:37:04.831 INFO 9092 --- [ task-2] com.chenpi.MyApplicationListener01 : >>> otherEventMyApplicationEvent{myField='我是自定义事件属性01', source=我是事件源01}

版权声明:本文为luciochn原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/luciochn/p/14940123.html