SpringBoot中启动加载类

681 阅读4分钟

「这是我参与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方法后,启动项目,可以看到如下效果,表示方法在项目启动后自动执行,并执行方法中内容。 image.png

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接口的类在项目启动后输出结果如下图:

image.png

3. ApplicationRunner和CommandLineRunner

3.1 两者的区别

如果项目中同时实现了ApplicationRunner和CommandLineRunner两个接口,则在项目启动后会有:

  1. 会先执行ApplicationRunner实现类中run()方法,后执行CommandLineRunner实现类中run()方法;
  2. ApplicationRunner实现类的run()方法执行完之前,不会执行CommandLineRunner实现类的run()方法。

尽管两个接口十分相似,且都需要重写有参的run()方法,但不同的是ApplicationRunner接口的run()方法参数类型是ApplicationArguments对象,而CommandLineRunner中run()方法的参数类型是String。

如图所示为在启动程序时传入了参数信息,则参数会相应的传入到两个接口对应实现类的run()方法中。

image.png

可以得到执行的结果信息:

image.png

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),则可以得到与默认顺序相反的的执行顺序结果。

image.png

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标注的方法。

执行结果:(在容器加在完成后执行,在项目最终启动之前)

image.png