程序员子龙(Java面试 + Java学习) 程序员子龙(Java面试 + Java学习)
首页
学习指南
工具
开源项目
技术书籍

程序员子龙

Java 开发从业者
首页
学习指南
工具
开源项目
技术书籍
  • 基础

  • JVM

  • Spring

  • 并发编程

  • Mybatis

  • 网络编程

  • 数据库

  • 缓存

  • 设计模式

  • 分布式

  • 高并发

  • SpringBoot

    • SpringBoot 整合redis
    • SpringBoot 线程池
    • springboot下整合mybatis
    • spring boot 配置文件的加载顺序
    • springboot启动不加载bootstrap.yml文件的问题解决
    • SpringBoot设置动态定时任务
    • springboot整合hibernate
    • ApplicationRunner、InitializingBean、@PostConstruct使用详解
      • 概述
      • CommandLineRunner和ApplicationRunner区别
        • ApplicationRunner接口的示例
        • CommandLineRunner接口示例
        • CommandLineRunner和ApplicationRunner的执行顺序
        • 实现多个ApplicationRunner时部分接口未执行
      • ApplicationRunner、InitializingBean、@PostConstruct执行顺序问题
        • InitializingBean接口的用法
        • @PostConstruct注解的用法
    • Spring Boot 优雅的参数校验方案
    • ELK处理 SpringBoot 日志,太优雅了!
    • SpringBoot配置数据源
    • Spring Boot 默认数据库连接池 —— HikariCP
    • 数据库连接池Hikari监控
    • Spring Boot中使用AOP统一处理Web请求日志
    • SpringBoot 三大开发工具,你都用过么?
    • Spring Boot 3.2 + CRaC = 王炸!
    • springboot启动的时候排除加载某些bean
    • spring boot中集成swagger
    • springboot项目引入这个包以后把原来的json报文改为了xml格式返回
    • SpringBoot中new对象不能自动注入对象和属性的问题
    • 使用 Spring Boot Actuator 监控应用
    • 记录一次springboot自动任务线上突然不执行问题排查过程
    • SpringBoot定时任务@Scheduled源码解析
    • Spring Boot + Lua = 王炸!
    • Spring Boot 实现定时任务动态管理
    • SpringBoot的@Async注解有什么坑?
    • druid 参数配置详解
    • Spring Boot HandlerMethodArgumentResolver 使用和场景
    • SpringBoot数据加解密
    • 解决controller层注入的service为null
    • 在 Spring Boot 中通过 RequestBodyAdvice 统一解码请求体
    • SpringBoot之使用Redisson实现分布式锁(含完整例子)
  • SpringCloudAlibaba

  • Nginx

  • 面试

  • 生产问题

  • 系统设计

  • 消息中间件

  • Java
  • SpringBoot
程序员子龙
2024-01-29
目录

ApplicationRunner、InitializingBean、@PostConstruct使用详解

# 概述

开发中可能会有这样的场景,需要在容器启动的时候执行一些内容。比如读取配置文件,数据库连接之类的。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);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

启动时候指定参数: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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

启动时候指定参数: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");
    }
}
1
2
3
4
5
6
7
8
@Component
@Order(4)
public class ApplicationRunnerBean2 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments arg0) throws Exception {
        System.out.println("ApplicationRunnerBean 2");
    }
}
1
2
3
4
5
6
7
8
@Component
@Order(1)
public class CommandLineRunnerBean1 implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("CommandLineRunnerBean 1");
    }
}
1
2
3
4
5
6
7
8
@Component
@Order(3)
public class CommandLineRunnerBean2 implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("CommandLineRunnerBean 2");
    }
}
1
2
3
4
5
6
7
8

执行结果:

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);
        }
 
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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);
        }
 
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

只会执行RunnerTest1中方法。

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

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

# ApplicationRunner、InitializingBean、@PostConstruct执行顺序问题

# 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接口");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

PostConstruct 方法执行 InitializingBean 方法执行

这个是测试ApplicationRunner接口 这个是测试CommandLineRunn接口

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

上次更新: 2024/01/30, 15:08:57
springboot整合hibernate
Spring Boot 优雅的参数校验方案

← springboot整合hibernate Spring Boot 优雅的参数校验方案→

最近更新
01
一个注解,优雅的实现接口幂等性
11-17
02
MySQL事务(超详细!!!)
10-14
03
阿里二面:Kafka中如何保证消息的顺序性?这周被问到两次了
10-09
更多文章>
Theme by Vdoing | Copyright © 2024-2024

    辽ICP备2023001503号-2

  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式