前言

在我们用 springboot 搭建项目的时候,有时候会碰到在项目启动时初始化一些操作的需求 ,针对这种需求 spring boot为我们提供了以下几种方案供我们选择:

ApplicationRunner 与 CommandLineRunner 接口

Spring容器初始化时InitializingBean接口和@PostConstruct

Spring的事件机制

ApplicationRunner与CommandLineRunner

我们可以实现 ApplicationRunner 或 CommandLineRunner 接口, 这两个接口工作方式相同,都只提供单一的run方法,该方法在SpringApplication.run(…)完成之前调用,我们先来看看这两个接口

@FunctionalInterface

public interface CommandLineRunner {

/**

* Callback used to run the bean.

* @param args incoming main method arguments

* @throws Exception on error

*/

void run(String... args) throws Exception;

}

@FunctionalInterface

public interface ApplicationRunner {

/**

* Callback used to run the bean.

* @param args incoming application arguments

* @throws Exception on error

*/

void run(ApplicationArguments args) throws Exception;

}

都只提供单一的run方法,接下来我们来看看具体的使用

ApplicationRunner

构造一个类实现ApplicationRunner接口

@Component

@Order(1)

public class ApplicationRunner1 implements ApplicationRunner {

@Override

public void run(ApplicationArguments args) throws Exception {

System.out.println("\u001B[32m[>>> startup ApplicationRunner1 <<

}

}

很简单,首先要使用@Component将实现类加入到Spring容器中,如果有多个的话通过@Order(1)进行排序,然后实现其run方法实现自己的初始化数据逻辑就可以了

CommandLineRunner

对于这两个接口而言,我们可以通过Order注解或者使用Ordered接口来指定调用顺序, @Order() 中的值越小,优先级越高

@Component

@Order(1)

public class CommandLineRunner1 implements CommandLineRunner {

@Override

public void run(String... args) throws Exception {

System.out.println("\u001B[32m[>>> startup runner1 <<

}

}

同样需要加入到Spring容器中,CommandLineRunner的参数是最原始的参数,没有进行任何处理,ApplicationRunner的参数是ApplicationArguments,是对原始参数的进一步封装,如果有多个的话通过@Order(1)进行排序

ApplicationRunner和CommandLineRunner排序规则

通过Order指定顺序

Order值相同ApplicationRunner的实现优先执行

源码分析

从SpringApplication.run方法的第8步callRunners开始

public ConfigurableApplicationContext run(String... args) {

```

// 第八步:执行Runners

callRunners(context, applicationArguments);

```

return context;

}

private void callRunners(ApplicationContext context, ApplicationArguments args) {

List runners = new ArrayList<>();

//获取容器中所有的ApplicationRunner的Bean实例

runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());

//获取容器中所有的CommandLineRunner的Bean实例

runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());

//对runners集合进行排序

AnnotationAwareOrderComparator.sort(runners);

for (Object runner : new LinkedHashSet<>(runners)) {

if (runner instanceof ApplicationRunner) {

//执行ApplicationRunner的run方法

callRunner((ApplicationRunner) runner, args);

}

if (runner instanceof CommandLineRunner) {

//执行CommandLineRunner的run方法

callRunner((CommandLineRunner) runner, args);

}

}

}

很明显,是直接从Spring容器中获取ApplicationRunner和CommandLineRunner的实例,并调用其run方法,这也就是为什么我要使用@Component将ApplicationRunner和CommandLineRunner接口的实现类加入到Spring容器中了。

ApplicationRunner和CommandLineRunner实现类的差异点

执行优先级差异

run方法入参不一致

ApplicationRunner和CommandLineRunner实现类的相同点

调用点一样

实现方法名一样

InitializingBean

在spring初始化bean的时候,如果bean实现了 InitializingBean 接口,在对象的所有属性被初始化后之后才会调用afterPropertiesSet()方法

@Component

public class InitialingzingBeanTest implements InitializingBean {

@Override

public void afterPropertiesSet() throws Exception {

System.out.println("InitializingBean..");

}

}

我们可以看出spring初始化bean肯定会在 ApplicationRunner和CommandLineRunner接口调用之前。

@PostConstruct

@Component

public class PostConstructTest {

@PostConstruct

public void postConstruct() {

System.out.println("init...");

}

}

我们可以看到,只用在方法上添加@PostConstruct注解,并将类注入到Spring容器中就可以了。我们来看看@PostConstruct注解的方法是何时执行的

在Spring初始化bean时,对bean的实例赋值时,populateBean方法下面有一个initializeBean(beanName, exposedObject, mbd)方法,这个就是用来执行用户设定的初始化操作。我们看下方法体:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory

implements AutowireCapableBeanFactory {

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {

if (System.getSecurityManager() != null) {

AccessController.doPrivileged((PrivilegedAction) () -> {

// 激活 Aware 方法

invokeAwareMethods(beanName, bean);

return null;

}, getAccessControlContext());

}

else {

// 对特殊的 bean 处理:Aware、BeanClassLoaderAware、BeanFactoryAware

invokeAwareMethods(beanName, bean);

}

Object wrappedBean = bean;

if (mbd == null || !mbd.isSynthetic()) {

// 后处理器

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

}

try {

// 激活用户自定义的 init 方法

invokeInitMethods(beanName, wrappedBean, mbd);

}

catch (Throwable ex) {

throw new BeanCreationException(

(mbd != null ? mbd.getResourceDescription() : null),

beanName, "Invocation of init method failed", ex);

}

if (mbd == null || !mbd.isSynthetic()) {

// 后处理器

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

}

return wrappedBean;

}

}

我们看到会先执行后处理器然后执行invokeInitMethods方法,我们来看下applyBeanPostProcessorsBeforeInitialization

@Override

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)

throws BeansException {

Object result = existingBean;

for (BeanPostProcessor processor : getBeanPostProcessors()) {

Object current = processor.postProcessBeforeInitialization(result, beanName);

if (current == null) {

return result;

}

result = current;

}

return result;

}

@Override

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)

throws BeansException {

Object result = existingBean;

for (BeanPostProcessor processor : getBeanPostProcessors()) {

Object current = processor.postProcessAfterInitialization(result, beanName);

if (current == null) {

return result;

}

result = current;

}

return result;

}

获取容器中所有的后置处理器,循环调用后置处理器的postProcessBeforeInitialization方法,这里我们来看一个BeanPostProcessor

public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor

implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {

public CommonAnnotationBeanPostProcessor() {

setOrder(Ordered.LOWEST_PRECEDENCE - 3);

//设置初始化参数为PostConstruct.class

setInitAnnotationType(PostConstruct.class);

setDestroyAnnotationType(PreDestroy.class);

ignoreResourceType("javax.xml.ws.WebServiceContext");

}

}

在构造器中设置了一个属性为PostConstruct.class,再次观察CommonAnnotationBeanPostProcessor这个类,它继承自InitDestroyAnnotationBeanPostProcessor。InitDestroyAnnotationBeanPostProcessor顾名思义,就是在Bean初始化和销毁的时候所作的一个前置/后置处理器。查看InitDestroyAnnotationBeanPostProcessor类下的postProcessBeforeInitialization方法:

public class InitDestroyAnnotationBeanPostProcessor

implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable {

@Override

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());

try {

metadata.invokeInitMethods(bean, beanName);

}

catch (InvocationTargetException ex) {

throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());

}

catch (Throwable ex) {

throw new BeanCreationException(beanName, "Failed to invoke init method", ex);

}

return bean;

}

private LifecycleMetadata findLifecycleMetadata(Class> clazz) {

if (this.lifecycleMetadataCache == null) {

// Happens after deserialization, during destruction...

return buildLifecycleMetadata(clazz);

}

// Quick check on the concurrent map first, with minimal locking.

LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);

if (metadata == null) {

synchronized (this.lifecycleMetadataCache) {

metadata = this.lifecycleMetadataCache.get(clazz);

if (metadata == null) {

metadata = buildLifecycleMetadata(clazz);

this.lifecycleMetadataCache.put(clazz, metadata);

}

return metadata;

}

}

return metadata;

}

private LifecycleMetadata buildLifecycleMetadata(final Class> clazz) {

List initMethods = new ArrayList<>();

List destroyMethods = new ArrayList<>();

Class> targetClass = clazz;

do {

final List currInitMethods = new ArrayList<>();

final List currDestroyMethods = new ArrayList<>();

ReflectionUtils.doWithLocalMethods(targetClass, method -> {

if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {

//判断clazz中的methon是否有initAnnotationType注解,也就是PostConstruct.class注解

LifecycleElement element = new LifecycleElement(method);

//如果有就将方法添加进LifecycleMetadata中

currInitMethods.add(element);

if (logger.isTraceEnabled()) {

logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);

}

}

if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {

//判断clazz中的methon是否有destroyAnnotationType注解

currDestroyMethods.add(new LifecycleElement(method));

if (logger.isTraceEnabled()) {

logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);

}

}

});

initMethods.addAll(0, currInitMethods);

destroyMethods.addAll(currDestroyMethods);

targetClass = targetClass.getSuperclass();

}

while (targetClass != null && targetClass != Object.class);

return new LifecycleMetadata(clazz, initMethods, destroyMethods);

}

}

在这里会去判断某方法是否有PostConstruct.class注解,如果有,则添加到init/destroy队列中,后续一一执行。@PostConstruct注解的方法会在此时执行,我们接着来看invokeInitMethods

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory

implements AutowireCapableBeanFactory {

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)

throws Throwable {

// 是否实现 InitializingBean

// 如果实现了 InitializingBean 接口,则只掉调用bean的 afterPropertiesSet()

boolean isInitializingBean = (bean instanceof InitializingBean);

if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {

if (logger.isTraceEnabled()) {

logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");

}

if (System.getSecurityManager() != null) {

try {

AccessController.doPrivileged((PrivilegedExceptionAction) () -> {

((InitializingBean) bean).afterPropertiesSet();

return null;

}, getAccessControlContext());

}

catch (PrivilegedActionException pae) {

throw pae.getException();

}

}

else {

// 直接调用 afterPropertiesSet()

((InitializingBean) bean).afterPropertiesSet();

}

}

if (mbd != null && bean.getClass() != NullBean.class) {

// 判断是否指定了 init-method(),

// 如果指定了 init-method(),则再调用制定的init-method

String initMethodName = mbd.getInitMethodName();

if (StringUtils.hasLength(initMethodName) &&

!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&

!mbd.isExternallyManagedInitMethod(initMethodName)) {

// 利用反射机制执行

invokeCustomInitMethod(beanName, bean, mbd);

}

}

}

}

首先检测当前 bean 是否实现了 InitializingBean 接口,如果实现了则调用其 afterPropertiesSet(),然后再检查是否也指定了 init-method(),如果指定了则通过反射机制调用指定的 init-method()。

我们也可以发现@PostConstruct会在实现 InitializingBean 接口的afterPropertiesSet()方法之前执行

Spring的事件机制

基础概念

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

事件: ApplicationEvent ,继承自JDK的 EventObject ,所有事件都要继承它,也就是被观察者

事件发布者: ApplicationEventPublisher 及 ApplicationEventMulticaster 接口,使用这个接口,就可以发布事件了

事件监听者: ApplicationListener ,继承JDK的 EventListener ,所有监听者都继承它,也就是我们所说的观察者,当然我们也可以使用注解 @EventListener ,效果是一样的

事件

在Spring框架中,默认对ApplicationEvent事件提供了如下支持:

ContextStartedEvent:ApplicationContext启动后触发的事件

ContextStoppedEvent:ApplicationContext停止后触发的事件

ContextRefreshedEvent: ApplicationContext初始化或刷新完成后触发的事件 ;(容器初始化完成后调用,所以我们可以利用这个事件做一些初始化操作)

ContextClosedEvent:ApplicationContext关闭后触发的事件;(如 web 容器关闭时自动会触发spring容器的关闭,如果是普通 java 应用,需要调用ctx.registerShutdownHook();注册虚拟机关闭时的钩子才行)

构造一个类继承ApplicationEvent

public class TestEvent extends ApplicationEvent {

private String message;

public TestEvent(Object source) {

super(source);

}

public void getMessage() {

System.out.println(message);

}

public void setMessage(String message) {

this.message = message;

}

}

创建事件监听者

有两种方法可以创建监听者,一种是直接实现ApplicationListener的接口,一种是使用注解 @EventListener , 注解是添加在监听方法上的 ,下面的例子是直接实现的接口

@Component

public class ApplicationListenerTest implements ApplicationListener {

@Override

public void onApplicationEvent(TestEvent testEvent) {

testEvent.getMessage();

}

}

事件发布

对于事件发布,代表者是 ApplicationEventPublisher 和 ApplicationEventMulticaster ,ApplicationContext接口继承了ApplicationEventPublisher,并在AbstractApplicationContext实现了具体代码,实际执行是委托给ApplicationEventMulticaster(可以认为是多播)

下面是一个事件发布者的测试实例:

@RunWith(SpringRunner.class)

@SpringBootTest

public class EventTest {

@Autowired

private ApplicationContext applicationContext;

@Test

public void publishTest() {

TestEvent testEvent = new TestEvent("");

testEvent.setMessage("hello world");

applicationContext.publishEvent(testEvent);

}

}

利用ContextRefreshedEvent事件进行初始化操作

利用 ContextRefreshedEvent 事件进行初始化,该事件是 ApplicationContext 初始化完成后调用的事件,所以我们可以利用这个事件,对应实现一个 监听器 ,在其 onApplicationEvent() 方法里初始化操作

@Component

public class ApplicationListenerTest implements ApplicationListener {

@Override

public void onApplicationEvent(ContextRefreshedEvent event) {

System.out.println("容器刷新完成后,我被调用了..");

}

}

springboot初始化逻辑_SpringBoot——启动初始化数据相关推荐

  1. SpringBoot druid配置datasource启动初始化数据库连接

    1.问题场景: 1.1.服务在启动之后的第一笔或者几笔请求,处理时间较长,对于调用方来说超时,引发技术保底. 1.2.数据库连接信息配置错误,项目启动过程中却不产生任何异常,等到实际功能用到数据库了才 ...

  2. linux账户初始化文件,Linux启动初始化配置文件浅析

    1)/etc/profile   登录时,会执行. 全局(公有)配置,不管是哪个用户,登录时都会读取该文件. (2)/ect/bashrc   Ubuntu没有此文件,与之对应的是/ect/bash. ...

  3. springboot初始化逻辑_详解Spring Boot中初始化资源的几种方式

    假设有这么一个需求,要求在项目启动过程中,完成线程池的初始化,加密证书加载等功能,你会怎么做?如果没想好答案,请接着往下看.今天介绍几种在Spring Boot中进行资源初始化的方式,帮助大家解决和回 ...

  4. Springboot启动初始化

    sprongboot项目启动初始化加载方式 ApplicationRunner与CommandLineRunner接口 Spring Bean初始化的构造方法.PostConstruct.init-m ...

  5. Camera2架构学习(二)——CameraServer和CameraProvider的启动初始化

    上一章讲的是从Framework下发命令,通过AIDL连接到相机设备服务.这一章讲服务的注册与启动.       服务层位于Camera Framework与Camera Provider之间,作为一 ...

  6. java 项目启动初始化_Spring项目启动时执行初始化方法

    一.applicationContext.xml配置bean init-method="initKeyWord"> classpath:sensitive-word.xml ...

  7. java 项目启动初始化_Spring Boot解决项目启动时初始化资源的方法

    前言 在我们实际工作中,总会遇到这样需求,在项目启动的时候需要做一些初始化的操作,比如初始化线程池,提前加载好加密证书等.今天就给大家介绍一个 Spring Boot 神器,专门帮助大家解决项目启动初 ...

  8. BOOT ROM 初始化内容、启动设备、镜像烧写

       目录 1 启动方式 1.1 串行下载 1.2 内部 BOOT 模式 1.3 BOOT ROM 初始化内容 2 启动设备 3 镜像烧写 3.1 IVT 和 Boot Data 数据 3.2 DCD ...

  9. 学习Android启动初始化 App StartUp

    StartUp是为了App的启动提供的一套简单.高效的初始化方案. ContentProvider中初始化 在项目中会需要用到很多的第三方库,而很多第三方库都提供了显示的调用初始化接口,需要在Appl ...

最新文章

  1. xenserver 管理口显示重复IP的处理方法
  2. System.Transactions事务超时设置
  3. 深度学习之七:卷积神经网络
  4. php 表单跳转,html - 思路问题:php表单跳转
  5. Codeforces Round #470 (rated, Div. 2, based on VK Cup 2018 Round 1)B. Primal Sport
  6. c语言逻辑运算符编程,C语言之逻辑运算符详解
  7. 【LeetCode】查找只出现一次的数字算法
  8. php读取js为文本,PHP / JS文本差异
  9. Linux Shell 流程控制语句实例
  10. 分享一下滑动验证码的模拟滑动攻克
  11. vim 模式下的几个快捷用法
  12. python opencv 边缘检测 抠图,python和opencv实现抠图
  13. ★关于人类体质弱化的分析
  14. 什么是堆栈,堆和栈到底是不是一个概念
  15. QGIS编译---QGIS2.14.16 + Qt4.8.6 + VS2010 ---32位版本
  16. 信息安全数学基础-期中复习提纲
  17. STM32小项目之dht11在oled上显示温湿度
  18. Mac上的Redis客户端 G-dis
  19. android关闭手机偷录她人功能实现(退出程序依然录像)
  20. ubuntu从tty终端模式返回到图形桌面

热门文章

  1. 解决ccSvcHst.exe CPU占用超50%的问题,及其缘由
  2. 在Eclipse中如何让struts.xml显示提示
  3. Exception Error log
  4. LRU最近最少使用缓存集合
  5. Buck-Boost变换
  6. qt-制作生成dll动态链接库实例
  7. DeleteDC() 与 ReleaseDC() 的区别 [转]
  8. vue内容横向循环滚动_vue文字横向滚动公告
  9. css 大图保持宽高比压缩,css 保持宽高比缩放
  10. oracle数据库某一年的数据总量_Liunx静默安装Oracle数据库