在开发Spring Boot应用程序时,有时我们需要在启动时运行一段代码。这段代码可以是任何类型,从记录某些信息到设置数据库,cron作业等。我们不能仅将此代码放入构造函数中,因为所需的变量或服务可能尚未初始化。这可能导致空指针或其他一些异常。
由于多种原因,我们需要在应用程序启动时运行方法,
- 记录重要的事情或说应用程序已启动的消息
- 处理数据库或文件,建立索引,创建缓存等。
- 启动后台进程,例如发送通知,从某些队列中获取数据等。
1.使用CommandLineRunner界面
CommandLineRunner是一个弹簧启动功能界面,用于在应用程序启动时运行代码。它位于软件包org.springframework.boot下。
在上下文初始化后的启动过程中,spring boot使用提供给应用程序的命令行参数调用其run()方法。
要通知spring boot关于我们的commandlineRunner接口,我们可以实现它并在类上方添加@Component批注,或者使用@bean创建其bean。
实现CommandLineRunner接口的示例
@Component |
创建CommandLineRunner接口Bean的示例
@SpringBootApplication |
我们可以使用命令行或IDE运行应用程序。让我们举一个例子,当我们使用参数“ –status = running”运行应用程序时
mvn spring-boot:run -Dspring-boot.run.arguments="--status=running" |
要么
mvn package |
这将产生以下日志输出:
In CommandLineRunnerImpl |
如我们所见,该参数未解析,而是解释为单个值“ status = running”。
要访问已解析格式的命令行参数,我们需要使用ApplicationRunner接口。我们待会儿再看。
Spring Boot将CommandLineRunner接口添加到启动过程中。因此,在commandlinerRunner中引发异常将迫使Spring启动中止启动。
我们可以在一个应用程序中创建多个CommandLineRunner。使用Ordered接口或@Order批注,我们可以配置它们的运行顺序。较低的值表示较高的优先级。默认情况下,所有组件均以最低优先级创建。这就是为什么没有订单配置的组件将被最后调用的原因。
我们可以使用订单注释,如下所示
@Component |
2.ApplicationRunner接口
如前所述,要访问已解析的参数,我们需要使用ApplicationRunner接口。ApplicationRunner接口为运行方法提供ApplicationArguments而不是原始字符串数组。
ApplicationArguments是一个接口,可从org.springframework.boot软件包下的boot 1.3 srping中获得。
它提供了以下几种访问参数的方法
- String[] GetSourceArgs(): 提供传递给应用程序的未处理参数
- Set<String> getOptionNames() : 所有可选参数的名称,可选参数的前面都有“ - “如:-name =‘堆栈跟踪’
- List <String> getNonOptionArgs() : 返回未处理的非可选参数。不带“ — ”的参数
- boolean containsOption(String name): 检查在可选参数中是否存在名称
- List<String> getOptionValues(String name): 通过名称给出参数值
作为接口实现的应用程序运行器示例
让我们使用“ status = running –mood = happy 10 –20”参数运行以下程序,并了解输出
@Component |
输出:
ApplicationRunnerImpl Called |
CommandLineRunner和ApplicationRunner具有类似的功能,例如
run()方法中的异常将中止应用程序启动
可以使用Ordered接口或@Order批注来订购多个ApplicationRunner
需要注意的最重要一点是,命令在CommandLineRunners和ApplicationRunners之间共享。这意味着可以在commandlinerRunner和applicationRunner之间混合执行顺序。
3. Spring Boot中的应用程序事件
Spring框架在不同情况下触发不同事件。它还会在启动过程中触发许多事件。我们可以使用这些事件来执行代码,例如,在Spring Boot应用程序启动后,可以使用ApplicationReadyEvent执行代码。
如果我们不需要命令行参数,这是在应用程序启动后执行代码的最佳方法。
@Component |
输出:
Yaaah, I am running........
最重要的事件是
- ApplicationContextInitializedEvent :在准备ApplicationContext并调用ApplicationContextInitializers之后但在加载bean定义之前触发
- ApplicationPreparedEvent :在加载bean定义后触发
- ApplicationStartedEvent :在刷新上下文之后但在调用命令行和应用程序运行程序之前触发
- ApplicationReadyEvent :在调用任何应用程序和命令行运行程序之后触发
- ApplicationFailedEvent :如果启动时发生异常则触发
- 可以创建多个ApplicationListeners。可以使用@Order批注或Ordered接口对其进行订购。
该顺序与其他相同类型的ApplicationListener共享,但不与ApplicationRunners或CommandLineRunners共享。
4.方法上的@Postconstruct注解
可以使用@PostConstruct批注标记方法。每当使用此注释标记方法时,将在依赖项注入后立即调用该方法。
@PostConstruct方法链接到特定的类,因此它仅应用于特定于类的代码。每个类只有一个带有postConstruct批注的方法。
@Component |
输出:
PostContructImpl Constructor called |
需要注意的是,如果class被标记为lazy,则意味着在请求时创建了class。之后,将执行标有@postConstruct批注的方法。
标有postConstruct批注的方法可以具有任何名称,但是不能具有任何参数。它必须是无效的,并且不能是静态的。
请注意,@postConstruct批注是Java EE模块的一部分,在Java 9中被标记为已弃用,在Java 11中已被删除。我们仍然可以通过将java.se.ee添加到应用程序中来使用它。
5. InitializingBean接口
InitializingBean解决方案的工作原理与postConstruct批注完全相似。不必使用注释,我们必须实现InitializingBean接口。然后,我们需要重写afterPropertiesSet()方法。
InitializingBean是org.springframework.beans.factory包的一部分。
@Component |
您必须考虑如果同时使用@PostConstruct批注和InitializingBean会发生什么。那么在这种情况下,@PostConstruct方法将在InitializingBean的afterPropertiesSet()方法之前调用。
6. @bean注释的init属性
我们可以在@Bean批注中使用initMethod属性提供一种方法。bean初始化后将调用此方法。
initMethod中提供的方法必须为空,并且不能包含任何参数。此方法甚至可以是私有的。
public class BeanInitMethodImpl { |
输出:
yooooooooo......... someone called me |
如果您具有同一类的InitializingBean实现和@Bean批注的initMethod属性,则将InitializingBean的afterPropertiesSet方法称为initMethod。
结合不同的方法:最后,有时我们可能需要组合多个选项。然后,它们将按照以下顺序执行:
- Constructor
- PostContruct method
- afterPropertiesSet method
- Bean init Method
- ApplicationStartedEvent
- ApplicationRunner Or CommandLineRunner depends on Order
- ApplicationReadyEvent