Spring启动时实现初始化有哪些方式?

作者:pbxs 时间:2023-09-20 18:25:57 

一、Spring启动时实现初始化的几种方式

准确的说是spring容器实例化完成后,几种初始化的方式。为什么这么说呢?下看面示例:


@Slf4j
@Component
public class InitBeanDemo {
   @Autowired
   private Environment env;

public InitBeanDemo() {
       log.info("DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
       log.info("ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
   }

示例是在bean的构造方法里做一些初始化的工作,示例比较简单只做了日志打印。是理想很丰满,现实很骨感,报错了:Constructor threw exception; nested exception is java.lang.NullPointerException。
原因是,Environment尚未初始化完成。

接下来我们来探索一下 有哪些初始化方式能满足上面示例的需求。

二、构造方法里初始化

可以正常运行,在所有初始化方式里执行时机最早。原理是在InitBeanDemo实例化前就实例化了Environment。


@Component
public class InitBeanDemo {

private final Environment env;

@Autowired
   public InitBeanDemo (Environment environment) {
       this.env = environment;
       log.info("Constructor DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
       log.info("Constructor ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
   }
}

三、常规三件套

常规三件套:@PostConstruct、InitializingBean、initMethod。 如果你愿意的话,三种方式可以在同一个Bean下同时使用,执行的优先级@PostConstruct > InitializingBean > initMethod。

@PostConstruct注解

在一个可以被扫描到Bean里,添加一个public void xxx()方法并加上@PostConstruct注解,方法里编写需要初始化的逻辑。
同一个应用程序里可以有多个@PostConstruct注解,同一个Bean里也可以有多个@PostConstruct注解。


@Slf4j
@Component
public class InitBeanDemo {
   @Autowired
   private Environment env;

@PostConstruct
   public void init() {
       log.info("@PostConstruct DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
       log.info("@PostConstruct ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
   }

实现InitializingBean接口

实现InitializingBean接口,在afterPropertiesSet() 方法里编写需要初始化的逻辑。
同一个应用程序里可以有多个实现InitializingBean接口的类,执行时机会按类名的自然顺序排序。


@Slf4j
@Component
public class InitBeanDemo implements InitializingBean {
   @Autowired
   private Environment env;

@Override
   public void afterPropertiesSet() throws Exception {
       log.info("InitializingBean DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
       log.info("InitializingBean ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
   }
}

指定Bean的initMethod方法

使用@Bean注解的initMethod属性可用于Bean的初始化后执行的方法。initMethod必须是public void 的无参构造方法。


@Slf4j
public class InitBeanDemo implements InitializingBean {
   @Autowired
   private Environment env;

public void initMethod() {
       log.info("initMethod DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
       log.info("initMethod ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
   }

@Configuration
public class InitBeanConfig {

@Bean(initMethod="initMethod")
   public InitBeanDemo initBeanDemo () {
       return new InitBeanDemo();
   }

}

等同于 在XML 配置中的init-method属性:


<bean id="initBeanDemo" class="com.xxx.InitBeanDemo" init-method="initMethod"></bean>

四、自定义ApplicationListener监听

两种方式,一种实现接口,另一种使用注解。

实现ApplicationListener接口

监听ContextRefreshedEvent事件。


@Slf4j
@Component
public class InitBeanDemo implements ApplicationListener<ContextRefreshedEvent>{
   @Autowired
   private Environment env;

@Override
   public void onApplicationEvent(ContextRefreshedEvent event) {
       log.info("ApplicationListener DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
       log.info("ApplicationListener ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
   }
}

@EventListener注释

方法参数里指定ContextRefreshedEvent事件。


@Slf4j
@Component
public class InitBeanDemo {
   @Autowired
   private Environment env;

@EventListener
   public void onApplicationEvent2(ContextRefreshedEvent event) {
       log.info("@EventListener DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
       log.info("@EventListener ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
   }
}

五、Spring Boot提供的初始化接口

 ApplicationRunner接口


@Slf4j
@Component
public class InitBeanDemo implements ApplicationRunner {
   @Autowired
   private Environment env;

@Override
   public void run(ApplicationArguments args) throws Exception {
       log.info("ApplicationRunner: {}", args);
       log.info("ApplicationRunner: {}", args.getOptionNames());
       log.info("ApplicationRunner DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
       log.info("ApplicationRunner ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
   }
}

CommandLineRunner接口

可以在同一个应用程序上下文中定义多个CommandLineRunner bean,并且可以使用@Ordered接口或@Order注释进行排序。


@Slf4j
@Component
public class InitBeanDemo implements CommandLineRunner {
   @Autowired
   private Environment env;

@Override
   public void run(String... args) throws Exception {
       log.info("CommandLineRunner: {}", args);
       log.info("CommandLineRunner DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
       log.info("CommandLineRunner ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
   }
}

在同一个Bean里使用以上初始化方式的执行先后顺序

在同一个Bean里使用以上初始化方式,运行的日志片段:

2021-06-07 11:24:41|INFO |main|c.c.s.s.t.ConstructorInitDemo|Constructor DefaultProfiles: [default]
2021-06-07 11:24:41|INFO |main|c.c.s.s.t.ConstructorInitDemo|Constructor ActiveProfiles: [sit]
2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|@PostConstruct DefaultProfiles: [default]
2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|@PostConstruct ActiveProfiles: [sit]
2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|InitializingBean DefaultProfiles: [default]
2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|InitializingBean ActiveProfiles: [sit]
2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|initMethod DefaultProfiles: [default]
2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|initMethod ActiveProfiles: [sit]
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|@EventListener DefaultProfiles: [default]
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|@EventListener ActiveProfiles: [sit]
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationListener DefaultProfiles: [default]
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationListener ActiveProfiles: [sit]
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationRunner: org.springframework.boot.DefaultApplicationArguments@68bef3df
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationRunner: []
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationRunner DefaultProfiles: [default]
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationRunner ActiveProfiles: [sit]
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|CommandLineRunner: {}
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|CommandLineRunner DefaultProfiles: [default]
2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|CommandLineRunner ActiveProfiles: [sit]

也即 整篇文章整理的先后顺序。

来源:https://blog.csdn.net/WLQ0621/article/details/117661645

标签:Spring,初始化
0
投稿

猜你喜欢

  • Java深入了解数据结构之栈与队列的详解

    2022-03-24 08:12:27
  • SpringBoot Security权限控制自定义failureHandler实例

    2022-12-03 08:46:58
  • @CacheEvict 清除多个key的实现方式

    2023-11-21 08:28:04
  • 如何利用Java输出链表中倒数第k个结点

    2022-11-29 10:13:28
  • SpringCloud Eureka服务治理之服务注册服务发现

    2021-12-27 15:07:16
  • SpringMVC多个文件上传及上传后立即显示图片功能

    2021-06-26 12:41:08
  • Spring Cloud之服务监控turbine的示例

    2023-04-20 23:26:44
  • 解决mybatis 中collection嵌套collection引发的bug

    2023-03-20 20:55:39
  • Spring使用三级缓存解决循环依赖的问题

    2023-03-14 09:06:15
  • Spring Mvc中传递参数方法之url/requestMapping详解

    2021-11-03 05:48:46
  • 微服务通过Feign调用进行密码安全认证操作

    2023-07-30 02:43:38
  • Java GZip 基于内存实现压缩和解压的方法

    2023-05-24 12:47:29
  • Java 如何优雅的抛出业务异常

    2023-11-24 04:04:34
  • Android中Listview下拉刷新和上拉加载更多的多种实现方案

    2022-11-19 17:00:43
  • Java获取指定字符串出现次数的方法

    2022-05-11 16:06:23
  • Android框架Volley之利用Imageloader和NetWorkImageView加载图片的方法

    2023-07-06 00:21:08
  • Java编程Nashorn实例代码

    2022-07-30 05:28:21
  • Springboot 如何指定获取出 yml文件里面的配置值

    2022-08-29 21:04:48
  • GoLang与Java各自生成grpc代码流程介绍

    2021-06-20 09:28:50
  • Spring Security实现基于角色的访问控制框架

    2022-03-19 05:40:53
  • asp之家 软件编程 m.aspxhome.com