SpringApplication
SpringApplication类提供了一种便捷的方式来引导从main()方法启动的Spring应用程序。通常可以委托给静态SpringApplication.run方法来启动应用程序,如以下示例所示:
public static void main(String[] args) {
SpringApplication.run(MySpringConfiguration.class, args);
}默认情况下,显示INFO日志消息,包括一些相关的启动详细信息,例如启动应用程序的用户。如果需要打印除INFO以外的其他日志级别,则可以按照日志级别中的说明进行设置。使用主应用程序类包中的实现版本来确定应用程序版本。可以通过将spring.main.log-startup-info设置为false来关闭启动信息记录。同时还会关闭对应用程序活动配置文件的日志记录。
要在应用启动期时添加其他日志记录,可以在
SpringApplication的子类中重写logStartupInfo(boolean)
启动失败
如果应用启动失败,可以通过FailureAnalyzers提供额外的错误消息和解决问题的措施,比如:使用8080端口部署web应用,但是端口已经被占用,Spring会显示如下信息:
***************************
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.这是由于Spring自身提供了许多FailureAnalyzer实现来将应用程序启动时错误转化为人类可读的错误信息,所以我们也可以自己实现FailureAnalyzer捕获启动时异常。
通过设置应用程序日志等级为debug能获取到完整的异常信息:
logging.level.root=warn
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error或者:
java -jar myproject-0.0.1-SNAPSHOT.jar --debug延迟加载
SpringApplication允许延迟地初始化应用程序。启用延迟初始化后,将根据需要创建bean,而不是在应用程序启动期间创建bean。这样做的结果就是可以减少应用程序启动所花费的时间。在Web应用程序中,启用延迟初始化将导致许多与Web相关的Bean直到收到HTTP请求后才被初始化。
延迟初始化的缺点是,它可能会延迟发现应用程序问题的时间点。如果错误配置的Bean延迟初始化,则启动期间将不再发生故障,只有在初始化Bean时问题才会出现。同时还必须注意确保JVM具有足够的内存来容纳所有应用程序的bean,而不仅仅是启动期间初始化的bean。由于这些原因,默认情况下不会启用延迟初始化,因此建议在启用延迟初始化之前先对JVM的堆大小进行微调。
延迟加载可以通过SpringApplicationBuilder的lazyInitialization方法或者SpringApplication的setLazyInitialization方法进行手动设置,另一种方法就是使用配置文件进行配置:
spring:
main:
lazy-initialization: true如果要在对应用程序其余部分使用延迟初始化时禁用某些Bean的延迟初始化,则可以使用
@Lazy(false)注解将它们的延迟加载属性显式设置为false。
自定义SpringApplication
如果SpringApplication的默认设置不符合需求,则可以创建一个本地实例并对其进行自定义。例如,要关闭Spring的Banner:
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MySpringConfiguration.class);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);
}传递给
SpringApplication的构造函数参数是Spring bean的配置源(@ComponetScan注解)。在大多数情况下,它们是对@Configuration类的引用,但也可以是对XML配置或应扫描的程序包的引用。
链式调用API
如果需要构建ApplicationContext层次结构(具有父/子关系的多个上下文),或者需要使用“流式”API,则可以使用SpringApplicationBuilder。
public void static main(String[] args){
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.logStartupInfo(true)
.registerShutdownHook(true)
.run(args);
}创建父子
ApplicationContext时有一些限制。例如,Web组件必须包含在子上下文中,并且父上下文和子上下文都使用相同的环境。因为子ApplicationContext能获取到父ApplicationContext中的bean而父ApplicationContext不能获取到子ApplicationContext中的bean。
事件监听
除了通常的Spring Framework运行时事件(例如ContextRefreshedEvent)之外,SpringApplication在启动应用时还会发送一些其他应用程序事件。
有些事件在
ApplicationContext被创建完成之前发布的,所以如果通过@Bean的方式注册监听器是无法监听到这类事件。可以通过SpringApplication.addListeners(…)或者SpringApplicationBuilder.listeners(…)方在启动前法进行手动注册。如果需要自动完成这类监听器注册,可以通过
META-INF/spring.factories文件进行注册,在文件中加入:org.springframework.context.ApplicationListener=com.example.project.MyListener
应用事件按如下顺序发送:
ApplicationStartingEvent:在应用运行开始时发部,但在进行任何处理之前(监听器和初始化器initializer的注册除外)。ApplicationEnvironmentPreparedEvent:当Environment对象初始化完成进行使用时,但此时上下文还没有创建。ApplicationContextInitializedEvent:当ApplicationContext已经初始化完成并且ApplicationContextInitializers都已经被调用,此时bean定义还未被加载ApplicationPreparedEvent:在ioC容器刷新之前,bean定义被加载之后发布ApplicationStartedEvent:在ioC容器刷新之后,Application和Command-line Runner被调用之前发布。AvailabilityChangeEvent:带有LivenessState.CORRECT属性指示应用已经上线ApplicationReadyEvent:在所有的Application和Command-line Runner被调用之后发布AvailabilityChangeEvent:带有ReadinessState.ACCEPTING_TRAFFIC属性指示应用已经可以接受请求ApplicationFailedEvent:当应用启动异常时发布
上面的列表仅包括绑定到SpringApplication的SpringApplicationEvents。除这些之外,以下事件也将在ApplicationPreparedEvent之后和ApplicationStartedEvent之前发布:
WebServerInitializedEvent:当WebServer初始化完成时发布,ServletWebServerInitializedEvent和ReactiveWebServerInitializedEvent分别对应servlet和响应式(webFlux)应用。ContextRefreshedEvent:当ApplicationContext已经刷新完成(bean已经创建完成)
应用程序事件是通过使用Spring Framework的事件发布机制发送的。此机制的一部分确保在子级上下文中发布给监听器的事件也可以在任何父级上下文中发布给监听器。如果应用程序使用SpringApplication实例的层次结构,则监听器可能会收到同一类型的应用程序事件的多个实例。
如果需要监听器能够区分其上下文的事件和子级上下文的事件,需要注入其应用程序上下文,然后将注入的上下文与接收到事件所属的上下文进行比较。可以通过实现ApplicationContextAware来注入上下文,或者,如果监听器本身是bean,则可以使用@Autowired注入上下文。

