广告位联系
返回顶部
分享到

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

java 来源:互联网 作者:佚名 发布时间:2024-05-01 21:50:09 人浏览
摘要

场景 项目中,经常需要在启动过程中初始化一些数据,如从数据库读取一些配置初始化,或从数据库读取一些热点数据到redis进行初始化缓存。 方式一:实现CommandLineRunner 接口重写run方法逻辑

场景

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

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

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

1

2

3

4

5

6

7

8

9

10

11

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型数据。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@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()方法的参数不同:

1

2

3

4

5

6

7

8

9

10

11

12

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。

1

2

3

4

5

6

7

@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 注解的方法在实现上可以使用任意修饰符。

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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 的具体执行顺序

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@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();

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@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 并初始化相应的类,类的初始化过程会运行静态代码块。

1

2

3

4

5

6

@Component

public class WordInitConfig {

    static {

        WordSegmenter.segWithStopWords("初始化分词");

    }

}

所以得到结论

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


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计