一、问题

日常业务模块开发的过程中,难免会需要做一些全局任务、缓存、线程等的初始化工作,那么如何做呢?方法有很多,但具体又要怎么选择呢?

二、资源初始化

1、既然我们要做资源的初始化,那么就要了解一下springboot启动过程。

springboot手动加载外部类 springboot加载类的过程_spring

按照前面的分析,Spring-boot容器启动流程总体可划分为2部分:

  1. 执行注解:扫描指定范围下的bean、载入自动配置类对应的bean加载到IOC容器。
  2. man方法中具体SpringAppliocation.run(),全流程贯穿SpringApplicationEvent(经典的spring事件驱动模型),有6个子类:
1. ApplicationFailedEvent.class
2. ApplicationPreparedEvent.class
3. ApplicationReadyEvent.class
4. ApplicationStartedEvent.class
5. ApplicationStartingEvent.class
6. SpringApplicationEvent.class

2、CommandLineRunner和ApplicationRunner

由上可知,我们只要实现这两个接口中的任何一个就可以完成资源初始化任务,可以看到它们的加载是在容器完全启动之前。

区别是:前者的run方法参数是String...args,直接传入字符串,后者的参数是ApplicationArguments,对参数进行了封装。

功能上基本是一样的。同时我们也可以使用@Order注解来实现资源加载的顺序,值越小,优先级越高。实例如下:

@Component
@Order(1)
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("...init resources by implements CommandLineRunner");
    }
}
@Component
@Order(2)
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        System.out.println("...init resources by implements ApplicationRunner");
    }
}

3、@PostConstruct

在具体Bean的实例化过程中执行,@PostConstruct注解标记的方法,会在构造方法之后执行,

顺序:Constructor > @Autowired > @PostConstruct > 静态方法

所以这个注解就避免了一些需要在构造方法里使用依赖组件的尴尬(对应的还有@PreDestroy注解,在对象消亡之前执行,原理基本一样的)。

使用特点如下:

  • 只有一个非静态方法能使用此注解
  • 被注解的方法不得有任何参数
  • 被注解的方法返回值必须为void
  • 被注解方法不得抛出已检查异常
  • 此方法只会被执行一次

4、InitializingBean

InitializingBean 是 Spring 提供的一个接口,只包含一个方法afterPropertiesSet()。

凡是实现了该接口的类,当其对应的 Bean交由Spring管理后,当其必要的属性全部设置完成后,Spring会调用该Bean的afterPropertiesSet()方法。

在Bean在实例化过程中执执行顺序为:Constructor > @PostConstruct > InitializingBean > init-method

public class InitSequenceBean implements InitializingBean {   
    public InitSequenceBean() {   
       System.out.println("InitSequenceBean: constructor");   
    }   
      
    @PostConstruct  
    public void postConstruct() {   
       System.out.println("InitSequenceBean: postConstruct");   
    }   
      
    public void initMethod() {   
       System.out.println("InitSequenceBean: init-method");   
    }   
      
    @Override  
    public void afterPropertiesSet() throws Exception {   
       System.out.println("InitSequenceBean: afterPropertiesSet");   
    }   
}

5、ApplicationListener

ApplicationListener 就是spring的监听器,能够用来监听事件,属于典型的观察者模式。如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将会自动被触发。这种事件机制都必须要程序显式的触发。其中spring有一些内置的事件,当完成某种操作时会发出某些事件动作。比如监听ContextRefreshedEvent事件,当所有的bean都初始化完成并被成功装载后会触发该事件,实现ApplicationListener接口可以收到监听动作,然后可以写自己的逻辑。同样事件可以自定义、监听也可以自定义,完全根据自己的业务逻辑来处理。所以也能做到资源的初始化加载!

@Component
public class DataSourceInitListener implements ApplicationListener<ContextRefreshedEvent> {
        //ContextRefreshedEvent为启动事件
    private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceInitListener.class);
    @Autowired
    private SystemConfigService systemConfigService;
    @Autowired
    private ItemService itemService;
    @Autowired
    private SystemResultService systemResultService;
 
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
            //判断是否执行过,执行过则不再执行
        if(event.getApplicationContext().getParent() == null) {
            LOGGER.info("初始化systemConfig数据");
            systemConfigService.initConfig();
            LOGGER.info("初始化返回消息数据");
            systemResultService.initResult();
            LOGGER.info("系统初始化结束...........");
        }
    }
 
}