ApplicationRunner、InitializingBean、@PostConstruct执行顺序解读

 更新时间:2025年09月13日 11:35:57   作者:_江屿_  
Spring Boot提供CommandLineRunner与ApplicationRunner接口,用于启动时执行初始化任务,前者接收原始参数数组,后者封装为ApplicationArguments对象,两者执行顺序可通过@Order控制

概述

开发中可能会有这样的场景,需要在容器启动的时候执行一些内容。比如读取配置文件,数据库连接之类的。

SpringBoot给我们提供了两个接口来帮助我们实现这种需求。

两个启动加载接口分别是:CommandLineRunner和ApplicationRunner。

Spring 提供了接口 InitializingBean,jdk提供了@PostConstruct

CommandLineRunner和ApplicationRunner区别

CommandLineRunner和ApplicationRunner的作用是相同的。不同之处在于CommandLineRunner接口的run()方法接收String数组作为参数,即是最原始的参数,没有做任何处理;而ApplicationRunner接口的run()方法接收ApplicationArguments对象作为参数,是对原始参数做了进一步的封装。

当程序启动时,我们传给main()方法的参数可以被实现CommandLineRunner和ApplicationRunner接口的类的run()方法访问,即可接收启动服务时传过来的参数。我们可以创建多个实现CommandLineRunner和ApplicationRunner接口的类。为了使他们按一定顺序执行,可以使用@Order注解或实现Ordered接口。

ApplicationRunner接口的示例

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
@Order(value = 1)
public class JDDRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {

        System.out.println("这个是测试ApplicationRunner接口");
               String strArgs = Arrays.stream(arg0.getSourceArgs()).collect(Collectors.joining("|"));
        System.out.println("Application started with arguments:" + strArgs);
    }
}

启动时候指定参数:java -jar xxxx.jar data1 data2 data3

这个是测试ApplicationRunner接口
Application started with arguments:data1|data2|data3

CommandLineRunner接口示例

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class TestCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("这个是测试CommandLineRunn接口");
         String strArgs = Arrays.stream(args).collect(Collectors.joining("|"));
        System.out.println("Application started with arguments:" + strArgs);
    }
}

启动时候指定参数:java -jar xxxx.jar data1 data2 data3

运行结果:

这个是测试CommandLineRunn接口
Application started with arguments:data1|data2|data3

CommandLineRunner和ApplicationRunner的执行顺序

在spring boot程序中,我们可以使用不止一个实现CommandLineRunner和ApplicationRunner的bean。

为了有序执行这些bean的run()方法,可以使用@Order注解或Ordered接口。

@Component
@Order(2)
public class ApplicationRunnerBean1 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments arg0) throws Exception {
        System.out.println("ApplicationRunnerBean 1");
    }
}
@Component
@Order(4)
public class ApplicationRunnerBean2 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments arg0) throws Exception {
        System.out.println("ApplicationRunnerBean 2");
    }
}
@Component
@Order(1)
public class CommandLineRunnerBean1 implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("CommandLineRunnerBean 1");
    }
}
@Component
@Order(3)
public class CommandLineRunnerBean2 implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("CommandLineRunnerBean 2");
    }
}

执行结果:

CommandLineRunnerBean 1 ApplicationRunnerBean 1 CommandLineRunnerBean 2 ApplicationRunnerBean 2

实现多个ApplicationRunner时部分接口未执行

@Component
@Slf4j
public class RunnerTest1 implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {

        while (true) {
            System.out.println("this is RunnerTest1");
            Thread.sleep(100);
        }

    }
}
@Component
@Slf4j
public class RunnerTest2 implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {

        while (true) {
            System.out.println("this is RunnerTest2");
            Thread.sleep(100);
        }

    }
}

只会执行RunnerTest1中方法。

通过分析springboot启动的源码可以发现,在applicationContext容器加载完成之后,会调用SpringApplication类的callRunners方法:

该方法中会获取所有实现了ApplicationRunner和CommandLineRunner的接口bean,然后依次执行对应的run方法,并且是在同一个线程中执行。因此如果有某个实现了ApplicationRunner接口的bean的run方法一直循环不返回的话,后续的代码将不会被执行。

InitializingBean接口的用法

InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。

注意,实现该接口的最好加上Spring的注解注入,比如@Component

@PostConstruct注解的用法

如果想在生成对象时候完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。

优先级: Constructor >> @Autowired >> @PostConstruct

@Component
public class Test implements InitializingBean, ApplicationRunner, CommandLineRunner {

    @PostConstruct
    public void init(){
        System.out.println("PostConstruct 方法执行");
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean 方法执行");
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("这个是测试ApplicationRunner接口");

    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println("这个是测试CommandLineRunn接口");
    }
}
PostConstruct 方法执行 InitializingBean 方法执行
这个是测试ApplicationRunner接口 这个是测试CommandLineRunn接口

由此可知: @PostConstruct>InitializingBean>ApplicationRunner>CommandLineRunner

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Spring Boot中MongoTemplate从入门到实战深度解析

    Spring Boot中MongoTemplate从入门到实战深度解析

    本文介绍了Spring Data MongoDB中的MongoTemplate,包括主要特性、核心配置、基础CRUD、高级查询与聚合操作、GridFS操作、性能优化与监控以及最佳实践,通过全面的示例项目,展示了如何在Spring Boot应用中使用MongoTemplate进行数据库操作,感兴趣的朋友跟随小编一起看看吧
    2026-01-01
  • java map转Multipart/form-data类型body实例

    java map转Multipart/form-data类型body实例

    这篇文章主要介绍了java map转Multipart/form-data类型body实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-05-05
  • 关于java中多个JDK和切换版本介绍

    关于java中多个JDK和切换版本介绍

    大家好,本篇文章主要讲的是关于java中多个JDK和切换版本介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2022-01-01
  • Java8 新特性Lambda表达式实例详解

    Java8 新特性Lambda表达式实例详解

    这篇文章主要介绍了Java8 新特性Lambda表达式实例详解的相关资料,需要的朋友可以参考下
    2017-03-03
  • SpringBoot日志配置全过程

    SpringBoot日志配置全过程

    Spring Boot默认使用Logback作为日志框架,可以配置多种日志系统,包括JavaUtilLogging、CommonsLogging、Log4J及SLF4J,默认日志输出在控制台,可以通过配置文件将日志保存到文件中,日志级别包括TRACE、DEBUG、INFO、WARN、ERROR和FATAL
    2025-01-01
  • MyBatis实现模糊查询的几种方式

    MyBatis实现模糊查询的几种方式

    这篇文章主要介绍了MyBatis实现模糊查询的几种方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • SpringBoot @ComponentScan扫描的局限性方式

    SpringBoot @ComponentScan扫描的局限性方式

    文章总结:SpringBoot的@ComponentScan注解在扫描组件时存在局限性,只能扫描指定的包及其子包,无法扫描@SpringBootApplication注解自动配置的组件,使用@SpringBootApplication注解可以解决这一问题,它集成了@Configuration、@EnableAutoConfiguration
    2025-01-01
  • 基于mybatis 动态SQL查询总结

    基于mybatis 动态SQL查询总结

    这篇文章主要介绍了mybatis 动态SQL查询总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • java读取excel文件并复制(copy)文件到指定目录示例

    java读取excel文件并复制(copy)文件到指定目录示例

    这篇文章主要介绍了java读取excel文件并复制文件到指定目录示例,需要的朋友可以参考下
    2014-02-02
  • SpringBoot启动时如何修改上下文

    SpringBoot启动时如何修改上下文

    本文介绍了如何在Spring Boot启动时修改上下文,以便加载封装JAR中的国际化文件,通过在resources目录下的META-INF文件夹中的spring.factories文件中配置指定类,可以实现这一功能
    2024-11-11

最新评论