「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战」
项目开发中,经常会碰到需要在项目启动后自动执行一些方法,如缓存的预热等功能,在SpringBoot中就提供了方法来完成这种功能需求。
1. ApplicationRunner
1.1 ApplicationRunner的定义
ApplicationRunner是org.springframework.boot包中定义的接口,在使用时需要定义类实现ApplicationRunner接口,并实现接口中的run()方法。
SpringBoot中对ApplicationRunner接口的定义源码为:
@FunctionalInterface
public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception;
}
1.2 定义类实现ApplicationRunner
类实现ApplicationRunner接口时,需要将类注入到容器中,否则SpringBoot无法获取到定义类的对象并自动执行其中的run()方法。
@Component
public class AutoRunApplicationRunner implements ApplicationRunner {
private static final Logger log = LoggerFactory.getLogger(AutoRunApplicationRunner.class);
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("实现ApplicationRunner的run方法自动执行,时间:{}", new Date());
}
}
1.3 项目执行结果
定义类实现ApplicationRunner接口,重写其中的run方法后,启动项目,可以看到如下效果,表示方法在项目启动后自动执行,并执行方法中内容。
2. CommandLineRunner
2.1 CommandLineRunner接口定义
CommandLineRunner同样是org.springframework.boot包中定义的接口,在使用时需要定义类实现ApplicationRunner接口,并实现接口中的run()方法。
其中,CommandLineRunner接口的定义为:
@FunctionalInterface
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
与ApplicationRunner接口不同的是,CommandLineRunner接口接受的参数类型不同,可以传入多个参数。
2.2 定义实现类
定义类实现CommandLineRunner接口后,同样也需要将类注入到容器中,否则SpringBoot无法自动执行run()方法。
@Component
public class AutoRunCommandLineRunner implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(AutoRunCommandLineRunner.class);
@Override
public void run(String... args) throws Exception {
log.info("实现AutoRunCommandLineRunner的run方法自动执行,时间:{},参数内容:", new Date(),args);
}
}
2.3 执行结果
实现CommandLineRunner接口的类在项目启动后输出结果如下图:
3. ApplicationRunner和CommandLineRunner
3.1 两者的区别
如果项目中同时实现了ApplicationRunner和CommandLineRunner两个接口,则在项目启动后会有:
- 会先执行ApplicationRunner实现类中run()方法,后执行CommandLineRunner实现类中run()方法;
- ApplicationRunner实现类的run()方法执行完之前,不会执行CommandLineRunner实现类的run()方法。
尽管两个接口十分相似,且都需要重写有参的run()方法,但不同的是ApplicationRunner接口的run()方法参数类型是ApplicationArguments对象,而CommandLineRunner中run()方法的参数类型是String。
如图所示为在启动程序时传入了参数信息,则参数会相应的传入到两个接口对应实现类的run()方法中。
可以得到执行的结果信息:
3.2 @Order定义执行顺序
对于ApplicationRunner和CommandLineRunner的执行顺序并不是绝对的,除了默认的执行顺序外。还可以使用@Order注解来指定两种实现类的执行顺序。
@Order注解是spring框架中的org.springframework.core.annotation包提供的注解,详细信息:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
int value() default 2147483647;
}
由于默认的执行顺序是ApplicationRunner实现类的run()方法早于CommandLineRunner实现类的run()方法, 因此使用注解为CommandLineRunner的实现类标注@Order(value = 1),为ApplicationRunner的实现类使用注解@Order(value = 2),则可以得到与默认顺序相反的的执行顺序结果。
4. @PostContstruct
@PostContstruct注解是Java本身的javax.annotation包中提供的注解内容,用来修饰一个非静态的方法,并在类创建完成后执行被标注的方法。
在Spring中框架中,标注该注解的方法执行顺序在构造方法和属性注入之后,即类加载完成后执行。
4.1 注解定义
根据@PostContstruct注解的相关定义,可以看到该注解只可以标注于方法之上。
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}
4.2 @PostContstruct注解的使用
实际使用@PostContstruct注解时,需要定义一个非静态方法,并为方法使用注解,表示该方法会在当前类初始化完成后自动执行。
使用时需要注意: 因为注解是在类对象构造完成后执行,因此不可以标注在类的静态方法上
@Component
public class AutoRunUserAnnotation {
private static final Logger log = LoggerFactory.getLogger(AutoRunUserAnnotation.class);
@PostConstruct
public void init(){
log.info("方法使用@PostConstruct注解来自动执行,时间:{}", new Date());
}
}
在项目开发时,如果定义的类使用了容器类注解,Spring会在容器初识化时加载所有使用相关注解的类对象,在容器加载完毕后,完成bean对象的创建,会立即执行类对象中使用@PostContstruct标注的方法。
执行结果:(在容器加在完成后执行,在项目最终启动之前)