阅读 72

SpringApplicationRunListener 的使用

基本使用

(1)实现 SpringApplicationRunListener

package com.example.demo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;

@Slf4j
public class MySpringApplicationRunListener implements SpringApplicationRunListener {

    @Override
    public void running(ConfigurableApplicationContext context) {
        log.info("running");
    }
}

(2)META-INF/spring.factories 配置:

org.springframework.boot.SpringApplicationRunListener=\
com.example.demo.MySpringApplicationRunListener

启动出现如下异常:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot instantiate interface org.springframework.boot.SpringApplicationRunListener : com.example.demo.MySpringApplicationRunListener
    at org.springframework.boot.SpringApplication.createSpringFactoriesInstances(SpringApplication.java:475)
    at org.springframework.boot.SpringApplication.getSpringFactoriesInstances(SpringApplication.java:457)
    at org.springframework.boot.SpringApplication.getRunListeners(SpringApplication.java:445)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:328)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332)
    at com.example.demo.DemoApplication.main(DemoApplication.java:12)
Caused by: java.lang.NoSuchMethodException: com.example.demo.MySpringApplicationRunListener.<init>(org.springframework.boot.SpringApplication, [Ljava.lang.String;)
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at org.springframework.boot.SpringApplication.createSpringFactoriesInstances(SpringApplication.java:470)
    ... 6 more

(3)MySpringApplicationRunListener 新增构造函数

@Slf4j
public class MySpringApplicationRunListener implements SpringApplicationRunListener {

    // Spring 容器实例化使用
    public MySpringApplicationRunListener(SpringApplication application, String[] args) {
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        log.info("running");
    }
}

启动后,控制台输出如下:

2021-07-26 19:39:22.666  INFO 1272 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 1.8.0_292 on mk with PID 1272 (F:\tmp\demo\target\classes started by meikai in F:\tmp\demo)
2021-07-26 19:39:22.668  INFO 1272 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2021-07-26 19:39:23.208  INFO 1272 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=6c8dcb71-9100-3fb1-9449-64ede168f178
2021-07-26 19:39:23.383  INFO 1272 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-07-26 19:39:23.389  INFO 1272 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-07-26 19:39:23.390  INFO 1272 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.50]
2021-07-26 19:39:23.452  INFO 1272 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-07-26 19:39:23.452  INFO 1272 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 750 ms
2021-07-26 19:39:25.015  INFO 1272 --- [           main] o.s.cloud.commons.util.InetUtils         : Cannot determine local hostname
2021-07-26 19:39:25.084  INFO 1272 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-07-26 19:39:26.390  INFO 1272 --- [           main] o.s.cloud.commons.util.InetUtils         : Cannot determine local hostname
2021-07-26 19:39:26.397  INFO 1272 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 5.436 seconds (JVM running for 6.259)
2021-07-26 19:39:26.399  INFO 1272 --- [           main] c.e.demo.MySpringApplicationRunListener  : running

测试优先级

(1)在上文的基础上,再实现一个 SpringApplicationRunListener

package com.example.demo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;

@Slf4j
@Order(1)
public class MySpringApplicationRunListener2 implements SpringApplicationRunListener {

    // Spring 容器实例化使用
    public MySpringApplicationRunListener2(SpringApplication application, String[] args) {
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        log.info("running");
    }
}

(2)META-INF/spring.factories 配置如下:

org.springframework.boot.SpringApplicationRunListener=\
com.example.demo.MySpringApplicationRunListener,\
com.example.demo.MySpringApplicationRunListener2

启动后,控制台输出如下:

2021-07-26 19:48:38.858  INFO 17836 --- [           main] c.e.d.MySpringApplicationRunListener2    : running
2021-07-26 19:48:38.859  INFO 17836 --- [           main] c.e.demo.MySpringApplicationRunListener  : running

MySpringApplicationRunListener 没有配置 Order,默认值为 Integer.MAX_VALUE,比 MySpringApplicationRunListener2 配置的 Order 值 1 大,所以 MySpringApplicationRunListener2 的优先级更高。

监听 ApplicationReadyEvent 事件

源码解读

(1)在 spring-boot-2.5.3.jarMETA-INF/spring.factories 文件中有如下配置:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

(2)EventPublishingRunListener

package org.springframework.boot.context.event;

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    @Override
    public int getOrder() {
        // 优先级为 0,order 越小,优先级越高
        return 0;
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
        AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
    }

}

(3)AnnotationAwareOrderComparator

package org.springframework.core.annotation;

public class AnnotationAwareOrderComparator extends OrderComparator {

    /**
     * Shared default instance of {@code AnnotationAwareOrderComparator}.
     */
    public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();

    public static void sort(List<?> list) {
        if (list.size() > 1) {
            list.sort(INSTANCE);
        }
    }
}

package org.springframework.core;
public class OrderComparator implements Comparator<Object> {

    @Override
    public int compare(@Nullable Object o1, @Nullable Object o2) {
        return doCompare(o1, o2, null);
    }

    private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
        boolean p1 = (o1 instanceof PriorityOrdered);
        boolean p2 = (o2 instanceof PriorityOrdered);
        if (p1 && !p2) {
            return -1;
        }
        else if (p2 && !p1) {
            return 1;
        }

        int i1 = getOrder(o1, sourceProvider);
        int i2 = getOrder(o2, sourceProvider);
        return Integer.compare(i1, i2);
    }

    protected int getOrder(@Nullable Object obj) {
        if (obj != null) {
            Integer order = findOrder(obj);
            if (order != null) {
                return order;
            }
        }
        // 如果没有配置,默认返回 Integer.MAX_VALUE
        return Ordered.LOWEST_PRECEDENCE;
    }
}

测试

(1)监听 ApplicationReadyEvent 事件

@Component
@Slf4j
public class MyApplicationListener implements ApplicationListener<ApplicationReadyEvent> {

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        log.info("ApplicationListener event:{}", event);
    }

    @EventListener(classes = {ApplicationReadyEvent.class})
    public void listen(ApplicationReadyEvent event) {
        log.info("@EventListener event:{}", event);
    }
}

(2)在上文的基础上,再实现一个 SpringApplicationRunListener

@Slf4j
@Order(-1)
public class MySpringApplicationRunListener3 implements SpringApplicationRunListener {

    // Spring 容器实例化使用
    public MySpringApplicationRunListener3(SpringApplication application, String[] args) {
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        log.info("running");
    }
}

(3)META-INF/spring.factories 配置如下:

org.springframework.boot.SpringApplicationRunListener=\
com.example.demo.MySpringApplicationRunListener,\
com.example.demo.MySpringApplicationRunListener2,\
com.example.demo.MySpringApplicationRunListener3

控制台输出:

2021-07-26 21:57:35.481  INFO 14332 --- [           main] c.e.d.MySpringApplicationRunListener3    : running
2021-07-26 21:57:35.481  INFO 14332 --- [           main] com.example.demo.MyApplicationListener   : ApplicationListener event:org.springframework.boot.context.event.ApplicationReadyEvent[source=org.springframework.boot.SpringApplication@2c708440]
2021-07-26 21:57:35.481  INFO 14332 --- [           main] com.example.demo.MyApplicationListener   : @EventListener event:org.springframework.boot.context.event.ApplicationReadyEvent[source=org.springframework.boot.SpringApplication@2c708440]
2021-07-26 21:57:35.481  INFO 14332 --- [           main] c.e.d.MySpringApplicationRunListener2    : running
2021-07-26 21:57:35.481  INFO 14332 --- [           main] c.e.demo.MySpringApplicationRunListener  : running
  • MySpringApplicationRunListener 没有配置 Order,默认值为 Integer.MAX_VALUE
  • MySpringApplicationRunListener2 配置的 Order 值为 1
  • MySpringApplicationRunListener3 配置的 Order 值为 -1
  • EventPublishingRunListener 配置的 Order 值为 0

所以,执行顺序为:

  • (1) MySpringApplicationRunListener3
  • (2) EventPublishingRunListener,及其监听的 ApplicationReadyEvent 处理。
  • (3) MySpringApplicationRunListener2
  • (4) MySpringApplicationRunListener

执行顺序和控制台输出的顺序符合。

参考

作者:蓝笔头

原文链接:https://www.jianshu.com/p/c0e29d6c0274

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐