关注微信公众号:CodingTechWork,一起学习进步。

引言

  在使用Spring Boot搭建项目时,启动项目工程,经常遇到一些需要启动初始化数据或者资源的需求,比如提前加载某个配置文件内容,初始化某个信息、做好安全认证等。这里一起学习总结了几种初始化数据的方式。

@Bean注解配置

使用方式

  编写配置类,使用@Configuration@Bean注解进行初始化。

使用示例

package com.example.andya.demo.conf;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @author Andya* @create 2020-09-14 21:37*/
@Configuration
public class InitConfigTest {@Value("${key}")private String key;@Beanpublic String testInit(){System.out.println("init key: " + key);return key;}
}

ApplicationRunner接口

使用方式

  编写类去实现ApplicationRunner接口,实现run()方法,该方法在工程启动类的XXXApplicationSpringApplication.run(xxxApplication.class, args)方法之前,@Componet会在所有Spring的Beans初始化完成之后,执行完成。

使用示例

package com.example.andya.demo.service.initTest;import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;/*** @author Andya* @date 2020-09-14 21:37*/
@Component
public class ApplicationRunnerTest implements ApplicationRunner {@Value("${key}")private String key;@Overridepublic void run(ApplicationArguments applicationArguments) throws Exception {System.out.println("ApplicationRunner test: init key : " + key);}
}

CommandLineRunner接口

使用方式

  类似于ApplicationRunner接口,我们同样编写类去实现CommandLineRunner接口,实现run()方法,该方法在工程启动类的XXXApplicationSpringApplication.run(xxxApplication.class, args)方法之前,@Componet会在所有Spring的Beans初始化完成之后,执行完成。
  多个类实现接口后,可以通过@Order注解进行顺序的控制,@Order(n),n越小,启动执行的越早。

使用示例

示例1

package com.example.andya.demo.service.initTest;import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @author Andya* @create 2020-09-14 21:37*/
@Component
@Order(1)
public class CommandLineRunner1Test implements CommandLineRunner {@Value("${key}")private String key;@Overridepublic void run(String... strings) throws Exception {System.out.println("CommandLineRunner first init: 1)init key : " + key);}
}

示例2

package com.example.andya.demo.service.initTest;import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @author Andya* @create 2020-09-14 21:37*/
@Component
@Order(2)
public class CommandLineRunner2Test implements CommandLineRunner {@Value("${key}")private String key;@Overridepublic void run(String... strings) throws Exception {System.out.println("CommandLineRunner second init: 2)init key : " + key);}
}

两种接口分析

接口对比

  CommandLineRunnerApplicationRunner都是接口,只是内部参数不一样,前者的参数是最原始的参数String类型,无任何处理;后者是ApplicationArguments类型,对原始参数进行了封装处理。

执行结果

上述三种方式的执行结果示例

  .   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::        (v1.5.6.RELEASE)2020-09-14 21:45:28.403  INFO 25408 --- [           main] com.example.andya.demo.DemoApplication   : Starting DemoApplication on DESKTOP-KC40970 with PID 25408 (F:\selfcode\target\classes started by Hugh in F:\selfcode)
2020-09-14 21:45:28.406  INFO 25408 --- [           main] com.example.andya.demo.DemoApplication   : No active profile set, falling back to default profiles: default
2020-09-14 21:45:28.434  INFO 25408 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1542153: startup date [Tue Sep 15 11:28:28 CST 2020]; root of context hierarchy
2020-09-14 21:45:29.322  INFO 25408 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 9000 (http)
2020-09-14 21:45:29.327  INFO 25408 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-09-14 21:45:29.327  INFO 25408 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.16
2020-09-14 21:45:29.385  INFO 25408 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-09-14 21:45:29.385  INFO 25408 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 953 ms
2020-09-14 21:45:29.504  INFO 25408 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2020-09-14 21:45:29.506  INFO 25408 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2020-09-14 21:45:29.506  INFO 25408 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2020-09-14 21:45:29.506  INFO 25408 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2020-09-14 21:45:29.506  INFO 25408 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
init key: value
2020-09-14 21:45:29.818  INFO 25408 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1542153: startup date [Tue Sep 15 11:28:28 CST 2020]; root of context hierarchy
2020-09-14 21:45:29.857  INFO 25408 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/aopTest/sayHi/{name}],methods=[GET]}" onto public java.lang.String com.example.andya.demo.controller.AopController.sayHi(java.lang.String)
2020-09-14 21:45:29.857  INFO 25408 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello],methods=[GET]}" onto public java.lang.String com.example.andya.demo.controller.HelloController.hello()
2020-09-14 21:45:29.859  INFO 25408 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2020-09-14 21:45:29.859  INFO 25408 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2020-09-14 21:45:29.923  INFO 25408 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-09-14 21:45:29.923  INFO 25408 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-09-14 21:45:29.955  INFO 25408 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-09-14 21:45:30.064  INFO 25408 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2020-09-14 21:45:30.069  INFO 25408 --- [           main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2020-09-14 21:45:30.095  INFO 25408 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 9000 (http)
CommandLineRunner first init: 1)init key : value
CommandLineRunner second init: 2)init key : value
ApplicationRunner test: init key : value
2020-09-14 21:45:30.097  INFO 25408 --- [           main] com.example.andya.demo.DemoApplication   : Started DemoApplication in 1.911 seconds (JVM running for 2.601)
2020-09-14 21:46:00.004  INFO 25408 --- [pool-1-thread-1] .s.a.AnnotationAsyncExecutionInterceptor : No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either
2020-09-14 21:46:59.781  INFO 25408 --- [nio-9000-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2020-09-14 21:46:59.781  INFO 25408 --- [nio-9000-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2020-09-14 21:46:59.802  INFO 25408 --- [nio-9000-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 21 ms

源码分析

XxxApplication启动类

package com.example.andya.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

其中,在工程中,点击SpringApplication.run(DemoApplication.class, args);的run方法进行源码追踪。

SpringApplication类源码

 //构造函数1public SpringApplication(Object... sources) {this.bannerMode = Mode.CONSOLE;this.logStartupInfo = true;this.addCommandLineProperties = true;this.headless = true;this.registerShutdownHook = true;this.additionalProfiles = new HashSet();this.initialize(sources);}//构造函数2public SpringApplication(ResourceLoader resourceLoader, Object... sources) {this.bannerMode = Mode.CONSOLE;this.logStartupInfo = true;this.addCommandLineProperties = true;this.headless = true;this.registerShutdownHook = true;this.additionalProfiles = new HashSet();this.resourceLoader = resourceLoader;this.initialize(sources);}//initialize方法private void initialize(Object[] sources) {//source不为空时,保存配置类if (sources != null && sources.length > 0) {this.sources.addAll(Arrays.asList(sources));}//判断该应用是否为web应用this.webEnvironment = this.deduceWebEnvironment();//获取并保存容器初始化ApplicationContextInitializer类,通过SpringFactoriesLoader.loadFactoryNames方法//从META-INF/spring.factories路径中获取ApplicationContextInitializer集合this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));//获取并保存监听器 ApplicationListener类,同样的,通过SpringFactoriesLoader.loadFactoryNames方法//从META-INF/spring.factories路径中获取ApplicationListener集合this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));//通过堆栈追踪名为main方法,从而获取包含main方法的类this.mainApplicationClass = this.deduceMainApplicationClass();}//run()方法1public static ConfigurableApplicationContext run(Object source, String... args) {return run(new Object[]{source}, args);}//run()方法2public static ConfigurableApplicationContext run(Object[] sources, String[] args) {return (new SpringApplication(sources)).run(args);}//run()方法3,最底层public ConfigurableApplicationContext run(String... args) {//新建计时工具类StopWatchStopWatch stopWatch = new StopWatch();//启动计时工具类StopWatchstopWatch.start();ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null;//设置java.awt.headless的系统属性,如服务器不需要显示器就需要如此设置this.configureHeadlessProperty();//获取监听器SpringApplicationRunListeners,调用了getSpringFactoriesInstances()方法//该方法又调用了loadFactoryNames()方法从META-INF/spring.factories路径中获取SpringApplicationRunListeners集合SpringApplicationRunListeners listeners = this.getRunListeners(args);//启动监听器listeners.starting();try {//将args参数封装至ApplicationArguments中ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//准备环境:内部调用了this.configureEnvironment()和listeners.environmentPrepared()等方法ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);//从环境中获取Banner进行打印,可以自定义BannerBanner printedBanner = this.printBanner(environment);//创建Spring的容器context = this.createApplicationContext();//分析并诊断是项目启动否有问题new FailureAnalyzers(context);//准备容器上下文://1)设置容器环境:context.setEnvironment(environment);//2)设置beanNameGenerator和resourceLoader:this.postProcessApplicationContext(context);//3)初始化context并检测是否接受该类型容器:this.applyInitializers(context);//4)触发监听事件:listeners.contextPrepared(context);是一个空函数;//5)注册bean:通过context.getBeanFactory().registerSingleton()方法向容器注入springApplicationArguments和springBootBanner//6)获取sources:Set<Object> sources = this.getSources();//7)加载启动类,注入到容器内:this.load(context, sources.toArray(new Object[sources.size()]));//给loader设置beanNameGenerator,resourceLoader和environment//然后调用load()方法,按照不同的source类型加载class,resource,package,charSequence,超过这些范围的就抛出异常“Invalid source type xxx”//8)触发监听事件:listeners.contextLoaded(context);进行this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);//刷新Spring容器this.refreshContext(context);//从容器中获取所有的ApplicationRunner和CommandLineRunner进行回调callRunnerthis.afterRefresh(context, applicationArguments);//触发监听事件,所有的SpringApplicationRunListener进行callFinishedListener回调listeners.finished(context, (Throwable)null);//计时器停止stopWatch.stop();if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);}//整个SpringBoot工程启动完毕,返回启动的Ioc容器上下文return context;} catch (Throwable var9) {this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);throw new IllegalStateException(var9);}}protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {this.callRunners(context, args);}//callRunnersprivate void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList();runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);Iterator var4 = (new LinkedHashSet(runners)).iterator();while(var4.hasNext()) {Object runner = var4.next();if (runner instanceof ApplicationRunner) {this.callRunner((ApplicationRunner)runner, args);}if (runner instanceof CommandLineRunner) {this.callRunner((CommandLineRunner)runner, args);}}}

  在上述源码中,我们分析了SpringBoot启动的一个流程,其中,我们可以从源码的run()方法一层一层点击追踪查看到,ApplicationRunnerCommandLineRunner是在callRunners方法中执行的。

参考
jdk1.8

SpringBoot—项目启动时几种初始化操作及SpringApplication类详解相关推荐

  1. 如何在项目启动时就执行某些操作

    参考资料:如何在项目启动时就执行某些操作 在实际的项目开发中经常会遇到一些需要在项目启动的时候进行初始化操作的需求,比如初始化线程池,配置某些对象的序列化和反序列化方式,加载黑名单白名单,加载权限应用 ...

  2. Springboot项目启动前执行数据库初始化脚本

    背景:项目里面遇到了要在springboot项目启动前做数据库初始化的需求.总结一下几种方案: 1.使用flywaydb,启动工程的时候同时初始化脚本.集成倒是不难,主要是要解决bean的顺序加载问题 ...

  3. springboot项目启动时提示错误: 找不到或无法加载主类

    问题: springboot项目启动时提示错误: 找不到或无法加载主类 解决方法:

  4. java unsafe 详解_Java CAS操作与Unsafe类详解

    一.复习 计算机内存模型,synchronized和volatile关键字简介 二.两者对比 sychronized和volatile都解决了内存可见性问题 不同点: (1)前者是独占锁,并且存在者上 ...

  5. SpringBoot 项目启动时设置 http 代理

      SpringBoot 项目打包成可执行 jar 文件 test-1.0.jar,在生产环境需要通过 http 代理服务器访问外部地址时,需要在项目启动时设置 http 代理,后台启动 jar 文件 ...

  6. 处理Springboot项目启动时streamBridge.send导致的卡住卡死问题

    现象 我们的Spring Boot 项目启动时,偶现卡死的现象,启动到一半卡主不动了 2023-01-16 10:23:10.338 INFO 1 --- [ restartedMain] com.h ...

  7. Springboot项目启动的三种方式

    springboot的启动方式主要有三种 运行带有main方法类 配置请求参数 通过命令行 java -jar 的方式:java -jar xxx.jar --server.port=8081 jav ...

  8. SpringBoot项目启动执行任务的几种方式

    经过整理后得到以下几种常用方式,供大家参考. 1. 使用过滤器 init() :该方法在tomcat容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次.可以在这个方法中补充 ...

  9. SpringBoot项目启动时:Failed to initialize connector [Connector[HTTP/1.1-8080]]

    用MyEcplise2017启动SpringBoot项目时突然闪退,再启动运行项目时就报错 Failed to initialize connector [Connector[HTTP/1.1-808 ...

最新文章

  1. java 静态初始化 调用_java JVM-类加载静态初始化块调用顺序
  2. 转;VC++中Format函数详解
  3. 转先验概率、最大似然估计、贝叶斯估计、最大后验概率
  4. ajax封装 使用,AJAX封装类使用指南
  5. js 网页嵌套在div的方法
  6. 自己动手编译OpenJDK
  7. 超市也开始玩“内卷”?
  8. CTP Java 版(Swig)
  9. setInterval()与clearInterval()的用法
  10. linux之SQL语句简明教程---CONCATENATE
  11. epoll监听文件_linux网络编程之epoll源码重要部分详解
  12. bgp状态idle什么原因_27-高级路由:BGP状态
  13. 使用itext到处PDF,使用PDF模板导出PDF文件
  14. office安装错误“错误25004,您输入的产品密钥无法在此计算机上使用,-----------”
  15. 计算机远程控制设计,远程控制软件的设计与实现
  16. 多种调度算法的平均周转时间算例
  17. linux程序提示killed的原因
  18. 计算机基础进制的换算教案,计算机系统基础(二)数值进制和进制转换
  19. Ubuntu更新系统源时出现错误提示W: GPG 错误
  20. 【思维题 阈值 期望】10.3奥义商店

热门文章

  1. C++:explicit关键字
  2. linux mmap系统调用
  3. (STTN)Learning Joint Spatial-TemporalTransformations for Video Inpainting
  4. 将 EndNote 文献信息导出成 BibTeX 格式(可根据label排序)以及出现三个问号
  5. U-Net中的skip connection
  6. PhpStorm调用浏览器运行php文件
  7. mapbox 修改初始位置_一行代码教你如何随心所欲初始化Bert参数(附Pytorch代码详细解读)...
  8. uni-app + vue-cli3 安装axios、vant等依赖 - 操作篇
  9. 小c下载样式插件Xiaocstyle适用于emlog系统
  10. YzmCMS全新轻爽极简风格模版主题