springboot资源初始化的几种方式

摘要

在实际项目中,咱们须要在springboot服务启动后作一些初始化工做,例如线程池初始化、文件资源加载、常驻后台任务启动(好比kafka consumer)等。本文介绍3类初始化资源的方法:html

  • Spring Bean初始化的InitializingBean,init-method和PostConstruct
  • ApplicationRunnerCommandLineRunner接口
  • Spring的事件机制

方法1:spring bean 初始化

Spring 容器中的 Bean 是有生命周期的,Spring 容许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操做,经常使用的设定方式有如下三种:java

  • 经过实现 InitializingBean接口来定制初始化以后的操做方法;
  • 经过 <bean> 元素的 init-method属性指定初始化以后调用的操做方法;
  • 在指定方法上加上@PostConstruct 注解来指定该在初始化以后调用的方法

执行的前后顺序:构造方法 --> @PostConstruct注解的方法 --> afterPropertiesSet方法(InitializingBean接口) --> init-method指定的方法。详情参考Spring Bean 初始化之InitializingBean, init-method 和 PostConstructgit

方法2:ApplicationRunner与CommandLineRunner接口

CommandLineRunner/ApplicationRunner 接口的 Component 会在全部 Spring Beans都初始化以后,SpringApplication.run()以前执行,很是适合在应用程序启动之初进行一些数据初始化的工做。CommandLineRunnerApplicationRunner这两个接口工做方式相同,都只提供单一的run方法,惟一的区别是run方法的入参类型不一样,CommandLineRunner的参数是最原始的参数,没有进行任何处理,ApplicationRunner的参数是ApplicationArguments,是对原始参数的进一步封装。github

runner定义

如下定义了两个runner,其中Order注解指定了执行顺序,数字越小越先执行web

@Order(1)
@Component
@Slf4j
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) {
        log.info("MyCommandLineRunner run...");
    }
}
-------------------------------------------------------------------
@Order(2)
@Component
@Slf4j
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("MyApplicationRunner run...");
    }
}

源码跟踪

跟踪源码,看下CommandLineRunner/ApplicationRunner是如何被调用的,Springboot在启动的时候,都会构造一个SpringApplication实例,执行run方法,一路点进去后不难发现调用入口是SpringApplication.run方法中的callRunners(context, applicationArguments)spring

public ConfigurableApplicationContext run(String... args) {
        ......
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, exceptionReporters, ex);
            throw new IllegalStateException(ex);
        }
        listeners.running(context);
        return context;
    }

总结

  • 全部CommandLineRunner/ApplicationRunner的执行时间点是在SpringBoot应用的ApplicationContext彻底初始化开始工做以后,callRunners()能够看出是run方法内部最后一个调用的方法(能够认为是main方法执行完成以前最后一步)
  • 只要存在于当前SpringBoot应用的ApplicationContext中的任何CommandLineRunner/ApplicationRunner,都会被加载执行(无论你是手动注册仍是自动扫描去Ioc容器)

方法3:spring事件机制

Spring的事件机制其实是设计模式中观察者模式的典型应用。观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监听一个主题对象。这样一来,当被观察者状态改变时,须要通知相应的观察者,使这些观察者可以自动更新。segmentfault

Spring的事件驱动模型由三部分组成设计模式

  • 事件: ApplicationEvent,继承自JDK的EventObject,全部事件都要继承它,也就是被观察者
  • 事件发布者: ApplicationEventPublisherApplicationEventMulticaster接口,使用这个接口,就能够发布事件了
  • 事件监听者::ApplicationListener,继承JDK的EventListener,全部监听者都继承它,也就是咱们所说的观察者,固然咱们也可使用注解 @EventListener,效果是同样的

built-in事件

在Spring框架中,内置了4种事件:springboot

  • ContextStartedEvent:ApplicationContext启动后触发的事件
  • ContextStoppedEvent:ApplicationContext中止后触发的事件
  • ContextRefreshedEvent:ApplicationContext初始化或刷新完成后触发的事件;(容器初始化完成后调用,因此咱们能够利用这个事件作一些初始化操做)
  • ContextClosedEvent:ApplicationContext关闭后触发的事件;(如web容器关闭时自动会触发spring容器的关闭,若是是普通java应用,须要调用ctx.registerShutdownHook();注册虚拟机关闭时的钩子才行)

例子

ContextRefreshedEvent为例,建立一个listener很是简单app

@Component
@Slf4j
public class MyContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        log.info("ContextRefreshedEvent listen... ");
    }
}

MyContextRefreshListener监听了内置事件ContextRefreshedEvent,即容器初始化完成后调用,MyContextRefreshListener.onApplicationEvent会被调用,利用此特性能够作一些初始化工做

注意: 在传统的基于XML配置的Spring项目中会存在二次调用的问题,即调用两次该方法,缘由是在传统的Spring MVC项目中,系统存在两个容器,一个root容器,一个project-servlet.xml对应的子容器,在初始化这两个容器的时候都会调用该方法一次,因此有二次调用的问题,而对于基于Springboot的项目不存在这个问题

三种方式的执行顺序

方法1(spring bean初始化) --> spring事件ContextRefreshedEvent--> CommandLineRunner/ApplicationRunner,示例代码下载请戳代码下载地址

参考文档

相关文章
相关标签/搜索