SpringApplication类-1
SpringApplication类提供了一种方便的方法来引导从main()方法启动的Spring应用程序。在许多情况下,可以委托给静态SpringApplication.run方法,如下例所示:
public static void main(String[] args) { SpringApplication.run(MySpringConfiguration.class, args); }
当应用程序启动时,你应该看到类似于以下输出的内容:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: v2.3.3.RELEASE 2019-04-31 13:09:54.117 INFO 56603 --- [ main] o.s.b.s.app.SampleApplication : Starting SampleApplication v0.1.0 on mycomputer with PID 56603 (/apps/myapp.jar started by pwebb) 2019-04-31 13:09:54.166 INFO 56603 --- [ main] ationConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6e5a8246: startup date [Wed Jul 31 00:08:16 PDT 2013]; root of context hierarchy 2019-04-01 13:09:56.912 INFO 41370 --- [ main] .t.TomcatServletWebServerFactory : Server initialized with port: 8080 2019-04-01 13:09:57.501 INFO 41370 --- [ main] o.s.b.s.app.SampleApplication : Started SampleApplication in 2.992 seconds (JVM running for 3.658)
默认情况下,INFO级别的日志信息会显示,包括一些相关的启动详细信息,例如启动应用程序的用户。如果需要除INFO以外的日志级别,可以按照日志级别中的说明进行设置。应用程序版本是使用主应用程序类的包中的实现版本确定的。启动信息记录可以通过设置spring.main.log-startup-info=false。这还将关闭应用程序活动配置文件的日志记录。
要在启动期间添加额外的日志记录,可以在SpringApplication的子类中重写logStartupInfo(boolean)。
启动失败
如果应用程序无法启动,注册的FailureAnalyzer将有机会提供专用的错误消息和解决问题的具体操作。例如,如果你在端口8080上启动web应用程序,并且该端口已在使用中,你应该会看到类似以下消息:
*************************** APPLICATION FAILED TO START *************************** Description: Embedded servlet container failed to start. Port 8080 was already in use. Action: Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.
你可以添加许多自己的FailureAnalyzer实现。
如果没有故障分析器能够处理异常,你仍然可以显示完整的条件报告,以便更好地了解出了什么问题。为此,你需要为启用调试属性或启用调试日志记录org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener.
例如,如果你使用java-jar运行应用程序,则可以启用debug属性,如下所示:
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
延迟初始化
SpringApplication允许延迟初始化应用程序。启用延迟初始化后,bean将按需要创建,而不是在应用程序启动期间创建。因此,启用延迟初始化可以减少应用程序启动所需的时间。在web应用程序中,启用延迟初始化将导致许多与web相关的bean在收到HTTP请求之前不会被初始化。
延迟初始化的一个缺点是它会延迟发现应用程序的问题。如果延迟初始化配置错误的bean,那么在启动过程中就不会再发生故障,而问题只会在bean初始化时显现出来。还必须注意确保JVM有足够的内存来容纳应用程序的所有bean,而不仅仅是那些在启动期间初始化的bean。由于这些原因,默认情况下不会启用延迟初始化,建议在启用延迟初始化之前对JVM堆大小进行微调。
惰性初始化可以使用SpringApplicationBuilder上的lazyInitialization方法或SpringApplication上的setLazyInitialization方法以编程方式启用。或者,使用spring.main.lazy-initialization属性,如下所示:
spring.main.lazy-initialization=true
如果希望在对应用程序的其余部分使用lazy初始化时禁用某些bean的lazy初始化,可以使用@lazy(false)注释将其lazy属性显式设置为false。
自定义Banner
启动时打印的banner 可以通过添加banner .txt文件或通过设置 spring.banner.location属性设置为此类文件的位置。如果文件的编码不是UTF-8,则可以设置spring.banner.charset. 除了文本文件,还可以添加banner.gif, banner.jpg,或banner.png将图像文件设置为类路径,或设置spring.banner.image.location属性。图像被转换成ASCII艺术表现形式并打印在任何文本横幅上方。
在你的banner.txt文件,则可以使用以下任何占位符:
- ${application.version}:在MANIFEST.MF声明的应用版本号。例如,实现版本1.0打印为1.0。
- ${application.formatted-version}:在MANIFEST.MF声明的应用版本号,并格式化为显示(用括号包围,前缀为v)。例如(v1.0)。
- ${spring-boot.version}:Spring Boot 版本,如2.3.3.RELEASE
- ${spring-boot.formatted-version}:你使用的springboot版本,格式显示(用括号括起来,前缀为v),例如(v2.3.3.RELEASE)。
- ${Ansi.NAME}(${AnsiColor.NAME)、${AnsiBackground.NAME}、${AnsiStyle.NAME})
- ${application.title}:在MANIFEST.MF声明应用的标题,如Implementation-Title: MyApp,就会打印MyApp
如果要以编程方式生成banner ,则可以使用SpringApplication.setBanner(…)方法。使用org.springframework.boot.Banner接口并实现自己的printbranner()方法。
你也可以使用spring.main.banner-mode来确定是否必须打印在System.out(console),发送到配置的记录器(log),或根本不生成(off)。
打印的banner注册为singleton bean,名称如下:springBootBanner。
自定义SpringApplication
如果SpringApplication默认值不符合你的意愿,你可以创建一个本地实例并对其进行自定义。例如,要关闭banner,可以写下:
public static void main(String[] args) { SpringApplication app = new SpringApplication(MySpringConfiguration.class); app.setBannerMode(Banner.Mode.OFF); app.run(args); }
传递给SpringApplication的构造函数参数是springbean的配置源。在大多数情况下,这些都是对@Configuration类的引用,但也可以是对XML配置或应该扫描的包的引用。
Fluent Builder API
如果需要构建ApplicationContext层次结构(具有父/子关系的多个上下文),或者更喜欢使用“fluent”构建器API,那么可以使用SpringApplicationBuilder。
SpringApplicationBuilder允许你将多个方法调用链接在一起,并包括父方法和子方法,这些方法允许你创建层次结构,如下例所示:
new SpringApplicationBuilder() .sources(Parent.class) .child(Application.class) .bannerMode(Banner.Mode.OFF) .run(args);
创建ApplicationContext层次结构时有一些限制。例如,Web组件必须包含在子上下文中,父上下文和子上下文都使用相同的环境。
应用程序可用性
当部署到平台上时,应用程序可以使用Kubernetes探测器等基础设施向平台提供有关其可用性的信息。springboot包括对常用的“活跃”和“准备就绪”可用性状态的现成支持。如果您使用的是springboot的“actuator”支持,那么这些状态将作为健康端点组公开。
此外,还可以通过将ApplicationAvailability接口注入自己的bean来获取可用性状态。
活跃状态
应用程序的“活跃”状态表明其内部状态是否允许其正常工作,或者在当前出现故障时自行恢复。中断的“活动”状态意味着应用程序处于无法恢复的状态,基础结构应重新启动应用程序。
一般来说,“活跃”状态不应基于外部检查,例如健康检查。如果是这样,发生故障的外部系统(数据库、Web API、外部缓存)将触发整个平台的大规模重启和级联故障。
springboot应用程序的内部状态主要由Spring的ApplicationContext来表示。如果应用程序上下文已成功启动,springboot将假定应用程序处于有效状态。一旦上下文被刷新,应用程序就被认为是活动的。
准备就绪状态
应用程序的“就绪”状态指示应用程序是否准备好处理流量。失败的“准备就绪”状态告诉平台,它现在不应该将流量路由到应用程序。这通常发生在启动过程中,而CommandLineRunner和ApplicationRunner组件正在处理中,或者在应用程序认为它太忙而无法进行额外的通信时发生。
一旦调用了应用程序和命令行运行程序,就认为应用程序已经就绪,请参阅springboot应用程序生命周期和相关的应用程序事件。
预期在启动期间运行的任务应由CommandLineRunner和ApplicationRunner组件执行,而不是使用Spring组件生命周期回调,如@PostConstruct。
管理应用程序可用性状态
通过注入ApplicationAvailability接口并对其调用方法,应用程序组件可以随时检索当前可用性状态。更常见的情况是,应用程序希望监听状态更新或更新应用程序的状态。
例如,我们可以将应用程序的“就绪”状态导出到一个文件中
@Component public class ReadinessStateExporter { @EventListener public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) { switch (event.getState()) { case ACCEPTING_TRAFFIC: // create file /tmp/healthy break; case REFUSING_TRAFFIC: // remove file /tmp/healthy break; } } }
我们还可以在应用程序中断且无法恢复时更新应用程序的状态:
@Component public class LocalCacheVerifier { private final ApplicationEventPublisher eventPublisher; public LocalCacheVerifier(ApplicationEventPublisher eventPublisher) { this.eventPublisher = eventPublisher; } public void checkLocalCache() { try { //... } catch (CacheCompletelyBrokenException ex) { AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN); } } }
springboot提供Kubernetes HTTP探针,用于查看Actuator Health Endpoints的“活跃度”和“准备就绪”
应用程序事件和侦听器
除了常见的Spring框架事件,比如ContextRefreshedEvent,SpringApplication还发送一些附加的应用程序事件。
有些事件实际上是在创建ApplicationContext之前触发的,因此不能将侦听器注册为@Bean。你可以在使用SpringApplication.addListeners(…)方法或SpringApplicationBuilder.listeners(…)方法进行注册。
如果希望自动注册这些侦听器,而不管应用程序的创建方式如何,添加一个META-INF/spring.factories文件,并使用org.springframework.context.ApplicationListener键,如下例所示:
org.springframework.context.ApplicationListener=com.example.project.MyListener
应用程序事件在应用程序运行时按以下顺序发送:
- ApplicationStartingEvent在运行开始时发送,但在任何处理之前发送,侦听器和初始值设定项的注册除外。
- 当上下文中要使用的环境已知,但在创建上下文之前,将发送ApplicationEnvironmentPreparedEvent。
- 当准备好ApplicationContext并调用applicationContextInitializer时,但在加载任何bean定义之前,将发送一个ApplicationContextInitializedEvent。
- ApplicationPreparedEvent将在刷新开始之前但在加载bean定义之后发送。
- ApplicationStartedEvent将在刷新上下文之后,但在调用任何应用程序和命令行运行程序之前发送。
- AvailabilityChangeEvent紧接着用发送LivenessState。正确指示应用程序被视为活动的。
- 在调用任何应用程序和命令行运行程序后发送ApplicationReadyEvent。
- AvailabilityChangeEvent紧接着用发送ReadinessState.接受流量指示应用程序已准备好服务请求。
- 如果启动时出现异常,则发送ApplicationFailedEvent。
上面的列表只包括绑定到SpringApplication的SpringApplicationEvents。除此之外,还将在ApplicationPreparedEvent之后和ApplicationStartedEvent之前发布以下事件:
- WebServerInitializedEvent在Web服务器准备就绪后发送。ServletWebServerInitializedEvent和ReactiveWebServerInitializedEvent分别是servlet和reactive变量。
- 刷新ApplicationContext时发送ContextRefreshedEvent。
您通常不需要使用应用程序事件,但知道它们的存在会很方便。在内部,springboot使用事件来处理各种任务。
应用程序事件是使用Spring框架的事件发布机制发送的。此机制的一部分确保在子上下文中发布到侦听器的事件也会发布到任何祖先上下文中的侦听器。因此,如果应用程序使用SpringApplication实例的层次结构,侦听器可能会接收同一类型应用程序事件的多个实例。
为了允许侦听器区分其上下文的事件和子上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。上下文可以通过实现ApplicationContextAware注入,或者,如果侦听器是bean,则可以使用@Autowired来注入。
Web环境
SpringApplication试图代表您创建正确类型的ApplicationContext。用于确定WebApplicationType的算法如下:
- 如果存在spring mvc,则使用注释AnnotationConfigServletWebServerApplicationContext
- 如果spring mvc不存在而spring webflux存在,则使用AnnotationConfigReactiveWebServerApplicationContext
- 否则,将使用AnnotationConfigApplicationContext
这意味着,如果您在同一个应用程序中使用spring mvc和spring webflux的新WebClient,默认情况下将使用springmvc。您可以通过调用setWebApplicationType(WebApplicationType)轻松地覆盖它。
还可以完全控制通过调用setApplicationContextClass(…)使用的ApplicationContext类型。
通常需要调用setWebApplicationType(WebApplicationType.NONE)在JUnit测试中使用SpringApplication时。
访问应用程序参数
如果需要访问传递给的应用程序参数SpringApplication.run(…),您可以注入org.springframework.boot.ApplicationArguments bean。ApplicationArguments接口提供对原始字符串[]参数以及已解析的选项和非选项参数的访问,如下例所示:
import org.springframework.boot.*; import org.springframework.beans.factory.annotation.*; import org.springframework.stereotype.*; @Component public class MyBean { @Autowired public MyBean(ApplicationArguments args) { boolean debug = args.containsOption("debug"); List<String> files = args.getNonOptionArgs(); // if run with "--debug logfile.txt" debug=true, files=["logfile.txt"] } }
springboot还向Spring环境注册CommandLinePropertySource。这还允许您使用@Value注释注入单个应用程序参数。
使用ApplicationRunner 或 CommandLineRunner
如果需要在SpringApplication启动后运行某些特定代码,可以实现ApplicationRunner或CommandLineRunner接口。两个接口以相同的方式工作,并提供一个运行方法,该方法在前面被调用SpringApplication.run(…)完成。
CommandLineRunner接口以字符串数组的形式提供对应用程序参数的访问,而ApplicationRunner使用前面讨论的ApplicationArguments接口。以下示例显示了具有run方法的CommandLineRunner:
import org.springframework.boot.*; import org.springframework.stereotype.*; @Component public class MyBean implements CommandLineRunner { public void run(String... args) { // Do something... } }
如果定义了几个必须按特定顺序调用的CommandLineRunner或ApplicationRunner bean,则可以另外实现org.springframework.core.Ordered或使用org.springframework.core.annotation.Order(值越小越先执行)
应用退出
每个SpringApplication向JVM注册一个关闭钩子,以确保ApplicationContext在退出时正常关闭。所有标准的Spring生命周期回调(例如DisposableBean接口或@PreDestroy注释)都可以使用。
另外,bean可以实现org.springframework.boot.ExitCodeGenerator接口,当SpringApplication.exit()被调用时期望返回一个特殊的退出编码,然后可以将此退出代码传递给System.exit()将其作为状态代码返回,如下例所示:
@SpringBootApplication public class ExitCodeApplication { @Bean public ExitCodeGenerator exitCodeGenerator() { return () -> 42; } public static void main(String[] args) { System.exit(SpringApplication.exit(SpringApplication.run(ExitCodeApplication.class, args))); } }
此外,ExitCodeGenerator接口可以通过异常实现。当遇到这样的异常时,springboot返回由实现的getExitCode()方法提供的退出代码。
管理功能
可以通过指定spring.application.admin.enabled属性启用与管理相关的特性。这将在平台MBeanServer上公开SpringApplicationAdminMXBean。您可以使用此功能远程管理springboot应用程序。这个特性对于任何服务包装器实现也很有用。
如果要知道应用程序在哪个HTTP端口上运行,请获取属性local.server.port.