由JDK提供了@PostConstruct注解,主要用于在Spring容器启动时执行某些操作或者任务,@PostConstruct注解一般放在BEAN的方法上,一旦BEAN初始化完成之后,将会调用这个方法。

一般在Spring框架的项目中使用到@PostConstruct注解时,该注解的方法在整个BEAN初始化中的执行顺序为:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)。

@PostConstruct:由JSR-250提供,在构造函数执行完之后执行该注解标注的方法,等价于XML配置文件中BEAN的initMethod方法;

@PreDestory:由JSR-250提供,在BEAN销毁之前执行该注解标注的方法,等价于XML配置文件中BEAN的destroyMethod方法。

有时针对一些特殊的业务场景,需要在系统启动时执行某些任务,如:配置文件的加载、数据库的初始化等等操作。SpringBoot 提供了两种解决方案:一种是使用CommandLineRunner,另一种是使用 ApplicationRunner。

基本原理

项目在启动时会遍历所有的 ApplicationRunner 的实现类并调用其中的 run 方法,如果在系统中有多个 ApplicationRunner的实现类,可以使用 @Order 注解对这些实现类的调用顺序进行排序(数字越小越先执行);

run方法的参数是系统启动时传入的参数,即入口类中main方法的参数(在调用SpringApplication.run方法时传入到 SpringBoot项目的上下文环境中)。

@Component
@Slf4j
@Order(1)
public class MyApplicationRunner implements ApplicationRunner {
// ApplicationArguments, 需要区分选项参数和非选项参数;
// 选项参数, 通过ApplicationArguments的getOptionNames()方法获取所有选项名称即参数的KEY, 然后通过 getOptionValues()方法根据参数KEY, 获取实际值(它会返回一个列表字符串), 一般为: --user-name=ROCKY --age=30
// 非选项参数, 通过ApplicationArguments的getNonOptionArgs()方法获取一个参数值数组;
@Override
public void run(ApplicationArguments args) throws Exception {
// TO DO SOMETHING...
}
}

ApplicationRunner和CommandLineRunner的区别

@Component
@Slf4j
@Order(1)
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// TO DO SOMETHING...
}
}

ApplicationRunner与CommandLineRunner的主要区别体现在run方法的参数上,CommandLineRunner中的run方法的参数是参数数组;ApplicationRunner中的run方法的参数是ApplicationArguments对象。


执行顺序

系统支持创建多个CommandLineRunner或ApplicationRunner的实现类,可以使用@Order注解或实现Ordered接口,来设定各个实现类的执行顺序。


选项参数与非选项参数

选项参数:可以理解为Spring Boot 提供的参数格式,以–开头,使用=分割键值对,如:java -jar XXX.jar --name=ROCKY --age=30 --spring.profiles.active=dev;

非选项参数:不是以–开头,也没有设置值的单一参数KEY只有值,如:java -jar XXX.jar --name=ROCKY --age=30 --spring.profiles.active=dev 陕西 西安 雁塔区,其中"陕西 西安 雁塔区" 就是非选项参数;

系统参数:-Dxxxx是设置JAVA运行上下文的参数语法,用于配置一些环境变量,如:java -jar XXX.jar -Dserver.port=8081 --name=ROCKY --age=30 --spring.profiles.active=dev 陕西 西安 雁塔区,其中"-Dserver.port"就是系统参数。

作用

InitializingBean的作用是Bean注入到Spring容器且初始化后,执行特定业务化的操作。Spring允许容器中的Bean,在Bean初始化完成后或者Bean销毁前,执行特定业务化的操作,常用的实现方式有以下三种:


通过实现InitializingBean/DisposableBean接口来处理初始化后/销毁前的操作;

通过标签的init-method/destroy-method属性处理初始化后/销毁前的操作;

在指定方法上加上@PostConstruct或@PreDestroy注解来处理初始化后/销毁前的操作。

实现

如果采用实现InitializingBean接口的方式去执行特定业务化的操作,则需要重写afterPropertiesSet这仅有的一个方法。

代码

package com.hadoopx.drools;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;

@Slf4j
@Data
public class KieAccessor implements InitializingBean {
private String path;
private String mode;
private Long update;
private String listener;
private String verify;

@Override
public void afterPropertiesSet() throws Exception {

if (StringUtils.isEmpty(path)) {
log.error("PLEASE SET THE RULE'S PATH: (spring.drools.path = XXX).");
}
}
}

注解源码

package javax.annotation;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}

使用场景

使用@PostConstruct注解能够在当前类加载时,为当前类初始化一些数据。通常在Service的实现类中,如果想在当前类加载的时候进行一系列的初始化操作,这时就可以使用@PostConstruct。@PostConstruct可以在容器没有完全启动的情况下能够进行初始化操作。


如何使用

①. 方式一

@Component
@Slf4j
public class SimpleExampleBean {
@PostConstruct
public void init(){
log.info("BEAN初始化完毕,调用INIT()...");
}
}

②. 方式二

@Slf4j
public class MySimpleExampleBean {
public void init(){
log.info("BEAN初始化完毕,调用INIT()...");
}
}

// 在配置类中通过@Bean实例化这个Bean,@Bean中的initMethod这个属性,需要指定初始化之后需要执行的方法。
@Bean(initMethod = "init")
public MySimpleExampleBean mySimpleExampleBean (){
return new MySimpleExampleBean();
}

使用总结

A. 注解修饰的方法是在依赖注入完成之后执行的;

B. 注解修饰的方法除了应用客户端外,不能是静态的;

C. 注解修饰的方法可以是final的;

D. 注解可以修饰一个非静态的void()方法;

E. 注解修饰的方法可以被public、protected、package private 或者private修饰。


关注公众号 soft张三丰 
SpringBoot - @PostConstruct 注解详解_spring