SpringBoot启动时执行初始化操作的几种方式

 更新时间:2024年05月01日 08:41:30   作者:strggle_bin  
项目中,经常需要在启动过程中初始化一些数据,如从数据库读取一些配置初始化,或从数据库读取一些热点数据到redis进行初始化缓存,本文给大家介绍了SpringBoot启动时执行初始化操作的几种方式的相关资料,需要的朋友可以参考下

场景

项目中,经常需要在启动过程中初始化一些数据,如从数据库读取一些配置初始化,或从数据库读取一些热点数据到redis进行初始化缓存。

方式一:实现CommandLineRunner 接口重写run方法逻辑

CommandLineRunner是Spring提供的接口,定义了一个run()方法,用于执行初始化操作。

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

@Component
public class InitConfigCommand implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner:"+"{}"+"接口实现方式重写");
    }
}

CommandLineRunner的执行时机为Spring beans初始化之后,因此CommandLineRunner的执行一定是晚于@PostConstruct的。

若有多组初始化操作,则每一组操作都要定义一个CommandLineRunner派生类并实现run()方法。这些操作的执行顺序使用@Order(n)来设置,n为int型数据。

@Component
@Order(99)
public class CommandLineRunnerA implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("初始化:CommandLineRunnerA");
    }
}

@Component
@Order(1)
public class CommandLineRunnerB implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("初始化:CommandLineRunnerB");
    }
}

如上,会先执行CommandLineRunnerB的run(),再执行CommandLineRunnerA的run()。

@Order(n)中的n较小的会先执行,较大的后执行。n只要是int值即可,无需顺序递增。

方式二:实现ApplicationRunner接口重写run方法逻辑

ApplicationRunner接口与CommandLineRunner接口类似,都需要实现run()方法。二者的区别在于run()方法的参数不同:

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

@Component
public class InitConfig implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("项目启动初始化");
    }
}

ApplicationRunner接口的run()参数为ApplicationArguments对象,因此可以获取更多项目相关的内容。

ApplicationRunner接口与CommandLineRunner接口的调用时机也是相同的,都是Spring beans初始化之后。因此ApplicationRunner接口也使用@Order(n)来设置执行顺序。

方式三:使用@PostConstruct注解的方式

对于注入到Spring容器中的类,在其成员函数前添加@PostConstruct注解,则在执行Spring beans初始化时,就会执行该函数。

但由于该函数执行时,其他Spring beans可能并未初始化完成,因此在该函数中执行的初始化操作应当不依赖于其他Spring beans。

@Component
public class Construct {
    @PostConstruct
    public void doConstruct() throws Exception {
        System.out.println("初始化:PostConstruct");
    }
}

初始化顺序

  • @PostConstruct 注解方法
  • CommandLineRunner接口实现
  • ApplicationRunner接口实现

扩展

@PostConstruct注解使用在方法上,它可以被用来标注一个非静态的 void 方法,这个方法会在该类被 Spring 容器初始化后立即执行。因为它的执行时机是在依赖注入之后,对象构造完成之后,也就是说是在@Autowired注入之后执行。所以这里可以进行一些初始化操作,如某些需要在对象创建后才能进行的数据初始化操作。

需要注意以下几点:

  1. @PostConstruct 只能用在方法上面,而不能用在属性或构造函数上。
  2. 一个类中可以有多个使用 @PostConstruct 注解的方法,但执行顺序并不是固定的。
  3. @PostConstruct 注解的方法在本类中必须是无参数的,如果有参数,那么这个方法不会被执行。
  4. @PostConstruct 注解的方法在实现上可以使用任意修饰符。

假设我们有一个需要初始化数据的类:

public class InitService {
 
    private List<String> data;
 
    public InitService() {
        this.data = Arrays.asList("A", "B", "C");
    }
 
    @PostConstruct
    public void init() {
        data.add("D");
    }
 
    public List<String> getData() {
        return this.data;
    }
}

当我们实例化 InitService 时,构造函数会为 data 属性赋初值,而 @PostConstruct 注解的 init 方法会在 Spring 容器实例化完 InitService 后被执行,将 “D” 添加到 data 列表中。所以当我们调用 getData() 方法时,返回的列表应该是 [A, B, C, D]。

接下来看看 @Autowired 和@PostConstruct 的具体执行顺序

@Service
public class TestA {
    static {
        System.out.println("staticA");
    }
    @Autowired
    private TestB testB;
 
    public TestA() {
        System.out.println("这是TestA 的构造方法");
    }
 
    @PostConstruct
    private void init() {
        System.out.println("这是TestA的 init 方法");
        testB.test();
    }
}
@Service
public class TestB {
    static {
        System.out.println("staticB");
    }
    @PostConstruct
    private void init() {
        System.out.println("这是TestB的init 方法");
    }
 
    public TestB() {
        System.out.println("这是TestB的构造方法");
    }
 
    void test() {
        System.out.println("这是TestB的test方法");
    }
}

构造方法:在对象初始化时执行。执行顺序在static静态代码块之后。

服务启动后,输出结果如下:

staticA
这是TestA 的构造方法
staticB
这是TestB的构造方法
这是TestB的init 方法
这是TestA的 init 方法
这是TestB的test方法

结论为:等@Autowired注入后,在执行@PostConstruct注解的方法。

方式四:静态代码块

static静态代码块,在类加载的时候即自动执行。

使用的static静态代码块,实现原理为@Component + static代码块, spring boot项目在启动过程中,会扫描@Component 并初始化相应的类,类的初始化过程会运行静态代码块。

@Component
public class WordInitConfig {
    static {
        WordSegmenter.segWithStopWords("初始化分词");
    }
}

所以得到结论

static>constructer>@Autowired>@PostConstruct>ApplicationRunner>CommandLineRunner

到此这篇关于SpringBoot启动时执行初始化操作的几种方式的文章就介绍到这了,更多相关SpringBoot执行初始化操作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java源码解析之接口Collection

    Java源码解析之接口Collection

    Collection是List、Queue和set的超集,它直接继承于Iterable,也就是说所有的Collection集合类都支持foreach循环.除此之外呢,Collection也是面向接口编程的典范,它可以在多种实现类间转换,这就是面向对象编程的厉害之处.接下来就随着小编一起去看看吧,需要的朋友可以参考下
    2021-05-05
  • 解决IDEA修改 .vmoptions 文件后导致无法启动的问题

    解决IDEA修改 .vmoptions 文件后导致无法启动的问题

    这篇文章主要介绍了解决IDEA修改 .vmoptions 文件后导致无法启动的问题,需要的朋友可以参考下
    2020-12-12
  • SpringBoot配置文件properties和yml的实现

    SpringBoot配置文件properties和yml的实现

    本文主要介绍了SpringBoot配置文件properties和yml的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Java基于final修饰数据过程解析

    Java基于final修饰数据过程解析

    这篇文章主要介绍了Java基于final修饰数据过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • SpringBoot接受参数相关注解方式

    SpringBoot接受参数相关注解方式

    SpringBoot接受参数的注解包括@PathVariable、@RequestHeader、@RequestParameter、@CookieValue、@RequestBody、@RequestAttribute和@SessionAttribute等,每个注解都有详细的使用方法和示例代码
    2024-12-12
  • Java基于Lock的生产者消费者模型示例

    Java基于Lock的生产者消费者模型示例

    这篇文章主要介绍了Java基于Lock的生产者消费者模型,结合实例形式分析了java基于锁机制的生产者消费者模型相关实现与使用技巧,需要的朋友可以参考下
    2018-08-08
  • Java借助Spire.PDF for Java高效实现PDF自动排版

    Java借助Spire.PDF for Java高效实现PDF自动排版

    在 Java 开发中,处理 PDF 文档的生成与编辑是常见的需求,本文将深入探讨如何利用 Java 高效准确地设置 PDF 中的文字对齐方式,感兴趣的小伙伴可以了解下
    2026-01-01
  • Java最常用的6个简单的计算题

    Java最常用的6个简单的计算题

    本篇文章给大家整理的在JAVA中最常用到的简单的计算题,对此有兴趣的朋友可以测试参考下。
    2018-02-02
  • SpringBoot+Caffeine+Redis实现多级缓存的方法

    SpringBoot+Caffeine+Redis实现多级缓存的方法

    多级缓存是指在系统中部署多层功能互补的缓存组件,让请求按照预设顺序依次访问各层缓存,仅当所有缓存层均未命中时,才访问底层数据库的架构模式,这篇文章主要介绍了SpringBoot+Caffeine+Redis实现多级缓存的方法,需要的朋友可以参考下
    2026-02-02
  • MyBatis-Plus高效开发实战指南

    MyBatis-Plus高效开发实战指南

    MyBatis-Plus(简称 MP)是⼀个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发,本文介绍MyBatis-Plus高效开发全攻略,感兴趣的朋友跟随小编一起看看吧
    2026-03-03

最新评论