41

补习系列(21)-SpringBoot初始化之7招式

 4 years ago
source link: https://www.tuicool.com/articles/2MVVf2f
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

JjQR3ia.jpg!web

背景

在日常开发时,我们常常需要 在SpringBoot 应用启动时执行某一段逻辑,如下面的场景:

  • 获取一些当前环境的配置或变量

  • 向数据库写入一些初始数据

  • 连接某些第三方系统,确认对方可以工作..

在实现这些功能时,我们可能会遇到一些"坑"。 为了利用SpringBoot框架的便利性,我们不得不将整个应用的执行控制权交给容器,于是造成了大家对于细节是一无所知的。 那么在实现初始化逻辑代码时就需要小心了,比如,我们并不能简单的将初始化逻辑在Bean类的构造方法中实现,类似下面的代码:

这里,我们在InvalidInitExampleBean的构造方法中试图访问一个自动注入的env字段,当真正执行时,你一定会得到一个空指针异常(NullPointerException)。

原因在于, 当构造方法被调用时,Spring上下文中的Environment这个Bean很可能还没有被实例化 ,同时也仍未注入到当前对象,所以并不能这样进行调用。

下面,我们来看看在SpringBoot中实现"安全初始化"的一些方法:

1、 @PostConstruct 注解

@PostConstruct 注解其实是来自于 javax的扩展包中(大多数人的印象中是来自于Spring框架),它的作用在于 声明一个Bean对象初始化完成后执行的方法

来看看它的原始定义:

The PostConstruct annotation is used on a method that needs to be executed  after dependency injection is done to perform any initialization

也就是说,该方法会在所有依赖字段注入后才执行,当然这一动作也是由Spring框架执行的。

下面的代码演示了使用@PostConstruct的例子:

2、 InitializingBean 接口

InitializingBean 是由Spring框架提供的接口,其与@PostConstruct注解的工作原理非常类似。 如果不使用注解的话,你需要让Bean实例继承 InitializingBean接口,并实现 afterPropertiesSet() 这个方法。

下面的代码,展示了这种用法:

3、 @Bean initMethod方法

我们在声明一个Bean的时候,可以同时指定一个initMethod属性,该属性会指向Bean的一个方法,表示在初始化后执行。

如下所示:

然后,这里将initMethod指向init方法,相应的我们也需要在Bean中实现这个方法:

上面的代码是基于Java注解的方式,使用Xml配置也可以达到同样的效果:

该方式在早期的 Spring版本中大量被使用

4、 构造器注入

如果依赖的字段在Bean的构造方法中声明,那么Spring框架会先实例这些字段对应的Bean,再调用当前的构造方法。 此时,构造方法中的一些操作也是安全的,如下:

5、 ApplicationListener

ApplicationListener 是由 spring-context组件提供的一个接口,主要是用来监听 "容器上下文的生命周期事件"。 它的定义如下:

这里的event可以是任何一个继承于ApplicationEvent的事件对象。 对于初始化工作来说,我们可以通过监听 ContextRefreshedEvent 这个事件来捕捉上下文初始化的时机。 如下面的代码:

在Spring上下文初始化完成后,这里定义的方法将会被执行。 与前面的InitializingBean不同的是,通过ApplicationListener监听的方式是全局性的,也就是当所有的Bean都初始化完成后才会执行方法。

Spring 4.2 之后引入了新的 @EventListener注解,可以实现同样的效果:

6、 CommandLineRunner

SpringBoot 提供了一个CommanLineRunner接口,用来实现在应用启动后的逻辑控制,其定义如下:

这里的run方法会在Spring 上下文初始化完成后执行,同时会传入应用的启动参数。 如下面的代码:

此外,对于多个CommandLineRunner的情况下可以使用@Order注解来控制它们的顺序。

7、 ApplicationRunner

与 CommandLineRunner接口类似, Spring boot 还提供另一个ApplicationRunner 接口来实现初始化逻辑。 不同的地方在于 ApplicationRunner.run()方法接受的是封装好的ApplicationArguments参数对象,而不是简单的字符串参数。

ApplicationArguments对象提供了一些非常方便的方法,可以用来直接获取解析后的参数,比如:

此时通过 ApplicationArguments的getOptionNames就会得到 ["debug","ip"] 这样的值。

测试代码

下面,通过一个小测试来演示几种初始化方法的执行次序。

按如下代码实现一个复合式的Bean:

执行这个Bean的初始化,会发现日志输出如下:

所以,这几种初始化的顺序为:

  1. 构造器方法

  2. @PostConstruct 注解方法

  3. InitializingBean的afterPropertiesSet()

  4. Bean定义的initMethod属性方法

参考文档

https://www.baeldung.com/running-setup-logic-on-startup-in-spring

点击查看完整的SpringBoot系列

敬请关注"美码师"公众号,笔者是十年老兵一枚,欢迎留言打扰,话题不限于技术、职场或生活..  

"写一首代码,做一手好菜",当技术与美走到一起时,生活也可以是诗和远方

7fUjay3.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK