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

    1. @Slf4j
    2. @Component
    3. public class InitBeanDemo {
    4. @Autowired
    5. private Environment env;
    6. public InitBeanDemo() {
    7. log.info("DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
    8. log.info("ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
    9. }
    10. }

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

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

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

    1. @Component
    2. public class InitBeanDemo {
    3. private final Environment env;
    4. @Autowired
    5. public InitBeanDemo (Environment environment) {
    6. this.env = environment;
    7. log.info("Constructor DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
    8. log.info("Constructor ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
    9. }
    10. }

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

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

    1. @Slf4j
    2. @Component
    3. public class InitBeanDemo {
    4. @Autowired
    5. private Environment env;
    6. @PostConstruct
    7. public void init() {
    8. log.info("@PostConstruct DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
    9. log.info("@PostConstruct ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
    10. }
    11. }

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

    1. @Slf4j
    2. @Component
    3. public class InitBeanDemo implements InitializingBean {
    4. @Autowired
    5. private Environment env;
    6. @Override
    7. public void afterPropertiesSet() throws Exception {
    8. log.info("InitializingBean DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
    9. log.info("InitializingBean ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
    10. }
    11. }

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

    1. @Configuration
    2. public class InitBeanConfig {
    3. @Bean(initMethod="initMethod")
    4. public InitBeanDemo initBeanDemo () {
    5. return new InitBeanDemo();
    6. }
    7. }

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

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

    自定义 ApplicationListener 监听
    两种方式,一种实现接口,另一种使用注解。

    实现 ApplicationListener 接口
    监听 ContextRefreshedEvent 事件。

    1. @Slf4j
    2. @Component
    3. public class InitBeanDemo implements ApplicationListener<ContextRefreshedEvent>{
    4. @Autowired
    5. private Environment env;
    6. @Override
    7. public void onApplicationEvent(ContextRefreshedEvent event) {
    8. log.info("ApplicationListener DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
    9. log.info("ApplicationListener ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
    10. }
    11. }

    @EventListener 注释
    方法参数里指定 ContextRefreshedEvent 事件。

    1. @Slf4j
    2. @Component
    3. public class InitBeanDemo {
    4. @Autowired
    5. private Environment env;
    6. @EventListener
    7. public void onApplicationEvent2(ContextRefreshedEvent event) {
    8. log.info("@EventListener DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
    9. log.info("@EventListener ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
    10. }
    11. }

    Spring Boot 提供的初始化接口
    ApplicationRunner 接口

    1. @Slf4j
    2. @Component
    3. public class InitBeanDemo implements ApplicationRunner {
    4. @Autowired
    5. private Environment env;
    6. @Override
    7. public void run(ApplicationArguments args) throws Exception {
    8. log.info("ApplicationRunner: {}", args);
    9. log.info("ApplicationRunner: {}", args.getOptionNames());
    10. log.info("ApplicationRunner DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
    11. log.info("ApplicationRunner ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
    12. }
    13. }

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

    1. @Slf4j
    2. @Component
    3. public class InitBeanDemo implements CommandLineRunner {
    4. @Autowired
    5. private Environment env;
    6. @Override
    7. public void run(String... args) throws Exception {
    8. log.info("CommandLineRunner: {}", args);
    9. log.info("CommandLineRunner DefaultProfiles: {}", Arrays.asList(env.getDefaultProfiles()));
    10. log.info("CommandLineRunner ActiveProfiles: {}", Arrays.asList(env.getActiveProfiles()));
    11. }
    12. }

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

    1. 2021-06-07 11:24:41|INFO |main|c.c.s.s.t.ConstructorInitDemo|Constructor DefaultProfiles: [default]
    2. 2021-06-07 11:24:41|INFO |main|c.c.s.s.t.ConstructorInitDemo|Constructor ActiveProfiles: [sit]
    3. 2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|@PostConstruct DefaultProfiles: [default]
    4. 2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|@PostConstruct ActiveProfiles: [sit]
    5. 2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|InitializingBean DefaultProfiles: [default]
    6. 2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|InitializingBean ActiveProfiles: [sit]
    7. 2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|initMethod DefaultProfiles: [default]
    8. 2021-06-07 11:24:42|INFO |main|c.c.s.s.test.InitBeanDemo|initMethod ActiveProfiles: [sit]
    9. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|@EventListener DefaultProfiles: [default]
    10. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|@EventListener ActiveProfiles: [sit]
    11. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationListener DefaultProfiles: [default]
    12. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationListener ActiveProfiles: [sit]
    13. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationRunner: org.springframework.boot.DefaultApplicationArguments@68bef3df
    14. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationRunner: []
    15. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationRunner DefaultProfiles: [default]
    16. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|ApplicationRunner ActiveProfiles: [sit]
    17. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|CommandLineRunner: {}
    18. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|CommandLineRunner DefaultProfiles: [default]
    19. 2021-06-07 11:24:44|INFO |main|c.c.s.s.test.InitBeanDemo|CommandLineRunner ActiveProfiles: [sit]

    也即 整篇文章整理的先后顺序。
    ————————————————
    版权声明:本文为CSDN博主「pbxs」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/WLQ0621/article/details/117661645