网站首页 文章专栏 学习理解 Spring ApplicationListener,以及 ApplicationListener在soul网关的应用
ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。
如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。这种事件机制都必须需要程序显示的触发。
其中spring有一些内置的事件,当完成某种操作时会发出某些事件动作。比如监听ContextRefreshedEvent事件,当所有的bean都初始化完成并被成功装载后会触发该事件,实现ApplicationListener
同样事件可以自定义、监听也可以自定义,完全根据自己的业务逻辑来处理。
| Spring 内置事件 | 描述 |
| 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处理用户请求结束后,系统会自动触发该事件。 |
| ApplicationStartedEvent | spring boot启动开始时执行的事件 |
ApplicationListener是一个接口,里面只有一个onApplicationEvent方法。如果在上下文中部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到 ApplicationContext时,调用ApplicationContext.publishEvent()方法,这个bean得到通知。类似于Oberver设计模式。其接口定义如下:
@FunctionalInterface
public interface ApplicationListener extends EventListener {
void onApplicationEvent(E var1);
}1). 可以根据内置的事件类型,在ApplicationContext bean被初始化的时候做某些操作,发送消息,初始化bean等等
2). 可以在springboot初始化加载完成后,进行某些操作
3). 可以在web服务请求被响应后,做某些事件
4). 可以根据自定义事件类型,发布事件,监听到事件后触发某些操作等等,这也是最常见的使用
1). 体验spring 内置事件
定义ApplicationStartedEventListener监听器,实现ApplicationListener接口,且在接口泛型定义ApplicationStartedEvent,就是内置的事件。注意要使用 @Component将listener被springboot感知注入
@Component
public class ApplicationStartedEventListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
System.out.println("============ApplicationStartedEvent============");
System.out.println(event);
}
}2). 同样的代码我们多搞几份,更改监听的事件类型
@Component
public class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
System.out.println(contextClosedEvent);
System.out.println("=============contextClosedEvent............................");
}
}
@Component
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
System.out.println(contextRefreshedEvent);
System.out.println("=============contextRefreshedEvent............................");
}
}
@Component
public class RequestHandledEventListener implements ApplicationListener<RequestHandledEvent> {
@Override
public void onApplicationEvent(RequestHandledEvent requestHandledEvent) {
System.out.println(requestHandledEvent);
System.out.println("=============requestHandledEvent............................");
}
}以上的事件分别为:
1>. ApplicationStartedEvent:spring boot启动开始时执行的事件
2>. ContextClosedEvent: 一个已关闭的上下文到达生命周期末端
3>. ContextRefreshedEvent: ApplicationContext 被初始化或刷新时
4>. RequestHandledEvent: 在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件
然后启动项目
发现先走到了 ContextRefreshedEvent

然后走到 ApplicationStartedEvent

日志打印:
2021-01-25 19:37:17.598 INFO 10532 --- [ main] o.s.s.c.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2021-01-25 19:37:17.716 DEBUG 10532 --- [ main] inMXBeanRegistrar$SpringApplicationAdmin : Application Admin MBean registered with name 'org.springframework.boot:type=Admin,name=SpringApplication' org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3f1c5af9, started on Mon Jan 25 19:37:15 CST 2021] =============contextRefreshedEvent............................ 2021-01-25 19:38:01.149 INFO 10532 --- [ main] o.s.b.w.e.t.TomcatWebServer : Tomcat started on port(s): 8888 (http) with context path '' 2021-01-25 19:38:01.152 INFO 10532 --- [ main] y.TestserviceApplication : Started TestserviceApplication in 46.326 seconds (JVM running for 47.793) ============ApplicationStartedEvent============ org.springframework.boot.context.event.ApplicationStartedEvent[source=org.springframework.boot.SpringApplication@54089484]
然后请求一个web接口:
http://127.0.0.1:8888/test/get
@RestController
@RequestMapping("/test")
public class TestController {
@PostMapping("/get")
public BaseResponse get(){
Map res = new HashMap<>();
res.put("name","yx");
return BaseResponse.success(res);
}
}发现,先走到
res.put("name","yx");在返回response之前,进入事件监听

日志打印:
ServletRequestHandledEvent: url=[/test/get]; client=[127.0.0.1]; method=[POST]; servlet=[dispatcherServlet]; session=[null]; user=[null]; time=[22597ms]; status=[OK] =============requestHandledEvent............................
从上面的demo可以看出,几个事件都按照说明实际执行了。我们再来体验下自定义事件。
2). 自定义事件监听
1>. 定义事件类型
public class MyEvent extends ApplicationEvent {
private String name;
public MyEvent(Object source, String name) {
super(source);
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}2>. 定义listener
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent myEvent) {
System.out.println("===========自定义事件==========");
System.out.println(myEvent);
}
}要点:ApplicationListener的事件泛型为 MyEvent,MyEventListener 要加上@Component
3>. 发布事件
@Autowired
private ApplicationEventPublisher eventPublisher;
@PostMapping("/get")
public BaseResponse get(){
Map res = new HashMap<>();
res.put("name","yx");
eventPublisher.publishEvent(new MyEvent(res,"yx"));
return BaseResponse.success(res);
}4>. 效果

3). 基于注解实现
1>. 事件定义一样的
2>. listener定义不一样,不用实现接口了
@Component
public class MyEvent2Listener {
@EventListener(classes={MyEvent2.class})
public void listen(MyEvent2 event){
System.out.println("MyEvent2Listener。。监听到的事件:" + event);
}
}3>. 发布事件,使用注解实现的话,idea左侧会有提示具体的监听地方,点击就进去了,如下图 MyEvent2的发布

4>. 效果一样
1). 在用户使用注解 @SoulSpringMvcClient(path = "/test/**") 将用户端的路由信息同步到admin时,使用了ApplicationContext的ContextRefreshedEvent事件,作用是在bean初始化的时候,判断配置文件是否是全部代理,进行数据同步操作
@Slf4j
public class ContextRegisterListener implements ApplicationListener {
...
@Override
public void onApplicationEvent(final ContextRefreshedEvent contextRefreshedEvent) {
if (!registered.compareAndSet(false, true)) {
return;
}
if (soulSpringMvcConfig.isFull()) {
post(buildJsonParams(soulSpringMvcConfig.getContextPath()));
}
}
private void post(final String json) {
...
}
}2). 在admin模块大量使用自定义事件DataChangedEvent,在DataChangedEvent里面加入事件类型,配置组等概念
public class DataChangedEvent extends ApplicationEvent {
private DataEventTypeEnum eventType;
private ConfigGroupEnum groupKey;
}并在 DataChangedEventDispatcher 中实现 ApplicationListener接口,监听事件,并根据事件类型的不同进行具体的数据发送
@Component
public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean {
private ApplicationContext applicationContext;
private List listeners;
public DataChangedEventDispatcher(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void onApplicationEvent(final DataChangedEvent event) {
for (DataChangedListener listener : listeners) {
switch (event.getGroupKey()) {
case APP_AUTH:
listener.onAppAuthChanged((List) event.getSource(), event.getEventType());
break;
case PLUGIN:
listener.onPluginChanged((List) event.getSource(), event.getEventType());
break;
case RULE:
listener.onRuleChanged((List) event.getSource(), event.getEventType());
break;
case SELECTOR:
listener.onSelectorChanged((List) event.getSource(), event.getEventType());
break;
case META_DATA:
listener.onMetaDataChanged((List) event.getSource(), event.getEventType());
break;
default:
throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
}
}
}
...
}实现 InitializingBean 接口是为了在初始化的时候,根据配置文件,实例化相应的数据同步方式,也就是 定义成员变量 listeners
@Override
public void afterPropertiesSet() {
Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values();
this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans));
}具体的使用还有很多种,根据需求可以灵活做出调整,最常见的使用还是,定义事件,监听事件,统一处理
版权声明:本文由星尘阁原创出品,转载请注明出处!