参考文档:
七种方式教你在SpringBoot初始化时搞点事情

1. 容器刷新完成时

容器刷新完成:容器刷新成功说明所有 bean 初始化已经完成

1.1 ContextRefreshedEvent

容器刷新完成后会调用容器内所有实现了ApplicationListener<ContextRefreshedEvent>BeanonApplicationEvent方法,应用程序可以以此达到监听容器初始化完成事件的目的。

@Component
public class StartupApplicationListenerExample implements 
  ApplicationListener<ContextRefreshedEvent> {

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("onApplicationEvent");
    }
}

对于 spring web 项目(例如 springmvc)

系统会存在两个容器,一个是root application context,另一个就是我们自己的context(作为root application context的子容器)。如果按照上面这种写法,就会造成onApplicationEvent方法被执行两次。解决此问题的方法如下:

@Component
public class StartupApplicationListenerExample implements 
  ApplicationListener<ContextRefreshedEvent> {

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getApplicationContext().getParent() == null) {
            // root application context 没有parent
            LOG.info("onApplicationEvent");
        }
    }
}

对于 springboot 项目,只有一个容器,参考 SpringMvc在SpringBoot环境和Web环境中上下文的关系

1.2 CommandLineRunner

当容器上下文初始化完成之后,SpringBoot也会调用所有实现了CommandLineRunner接口的run方法,下面这段代码可起到和上文同样的作用:

@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {

    @Override
    public void run(String...args) throws Exception {
        LOG.info("run");
    }
}

对于这个扩展点的使用有额外两点需要注意:

  • 多个实现了CommandLineRunnerBean的执行顺序可以根据Bean上的@Order注解调整

  • run方法可以接受从控制台输入的参数,跟ApplicationListener<ContextRefreshedEvent>这种扩展相比,更加灵活

    // 从控制台输入参数示例
    java -jar CommandLineAppStartupRunner.jar abc abcd
    

1.3 ApplicationRunner

这个扩展和SpringBootCommandLineRunner接口的扩展类似,只不过接受的参数是一个ApplicationArguments类,对控制台输入的参数提供了更好的封装,以--开头的被视为带选项的参数,否则是普通的参数

@Component
public class AppStartupRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        LOG.info("Application started with option names : {}", args.getOptionNames());
    }
}
java -jar CommandLineAppStartupRunner.jar abc abcd --autho=mark verbose

2. bean 注入完成时

在 bean 中依赖注入完成后,类投入使用之前

2.1 @PostConstruct

@PostConstruct注解一般放在Bean的方法上,被@PostConstruct修饰的方法会在Bean初始化后马上调用:

@Component
public class PostConstructExampleBean {

    @Autowired
    private Environment environment;

    @PostConstruct
    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.2 InitializingBean 接口

InitializingBean的用法基本上与@PostConstruct一致,只不过相应的Bean需要实现afterPropertiesSet方法

@Component
public class InitializingBeanExampleBean implements InitializingBean {


    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.3 @Bean init 方法

通过@Bean注入Bean的时候可以指定初始化方法:

bean 的定义

public class InitMethodExampleBean {

    @Autowired
    private Environment environment;

    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

bean 的注入

@Bean(initMethod="init")
public InitMethodExampleBean initMethodExampleBean() {
    return new InitMethodExampleBean();
}

2.4 执行顺序

@Component
@Scope(value = "prototype")
public class AllStrategiesExampleBean implements InitializingBean {

    public AllStrategiesExampleBean() {
        LOG.info("Constructor");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info("InitializingBean");
    }

    @PostConstruct
    public void postConstruct() {
        LOG.info("PostConstruct");
    }

    public void init() {
        LOG.info("init-method");
    }
}
[main] INFO o.b.startup.AllStrategiesExampleBean - Constructor
[main] INFO o.b.startup.AllStrategiesExampleBean - PostConstruct
[main] INFO o.b.startup.AllStrategiesExampleBean - InitializingBean
[main] INFO o.b.startup.AllStrategiesExampleBean - init-method