通过SpringApplication类,你可以从main()方法中启动Spring应用程序。在许多情况下,你可以直接调用SpringApplication.run静态方法,如下例子
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
当你程序启动时可以看到类似以下的日志输出:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.2.0-SNAPSHOT) 2023-09-10T13:33:54.881+08:00 INFO 14060 --- [ main] o.s.b.d.f.s.MyApplication : Starting MyApplication using Java 17 with PID 14060 (/opt/apps/myapp.jar started by myuser in /opt/apps/) 2023-09-10T13:33:54.887+08:00 INFO 14060 --- [ main] o.s.b.d.f.s.MyApplication : No active profile set, falling back to 1 default profile: "default" 2023-09-10T13:33:56.187+08:00 INFO 14060 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2023-09-10T13:33:56.202+08:00 INFO 14060 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2023-09-10T13:33:56.202+08:00 INFO 14060 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.10] 2023-09-10T13:33:56.344+08:00 INFO 14060 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2023-09-10T13:33:56.347+08:00 INFO 14060 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1389 ms 2023-09-10T13:33:56.821+08:00 INFO 14060 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2023-09-10T13:33:56.831+08:00 INFO 14060 --- [ main] o.s.b.d.f.s.MyApplication : Started MyApplication in 2.379 seconds (process running for 2.715)
默认情况下,会显示INFO级别的日志信息,包括一些相关的启动细节,比如启动应用程序的用户。如果你需要INFO以外级别的日志,你可以设置它,如下面日志级别的配置,应用程序的版本时使用main方法所在类的包的实现版本来确定的。启动信息的 记录可以通过设置spring.main.log-startup-info为false来关闭。这也将光比应用程序的激活的profiles的日志记录。
logging:
level:
root: "warn"
org.springframework.web: "debug"
org.hibernate: "error"
1.1 启动失败
如果你的应用启动失败,注册的FailureAnalyzers会尝试提供一个专门的错误信息和具体的解决方法。例如,如果你在端口8080上启动一个网络应用,而该端口以及被使用,你应该看到类似于下面的错误。
*************************** APPLICATION FAILED TO START *************************** Description: Embedded servlet container failed to start. Port 8080 was already in use. Action: Identify and stop the process that is listening on port 8080 or configure this application to listen on another port.
✦ SpringBoot提供了许多FailureAnalyzer的实现,你也可以添加自己的实现。这里就不多说了。
如果failure analyzer不能够处理异常,你仍然可以显示完整的条件报告以更好地了解出错的原因。要实现这个,你需要为org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener启用debug属性或启用DEBUG日志。
例如,如果你通过使用java -jar 来运行你的应用程序,你可以按以下方式启用debug属性。
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
1.2 懒初始化(Lazy Initialization)
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
✦ 如果你想禁用某些Bean的懒初始化,同时对应用程序的其他部分使用懒初始化,你可以使用@Lazy(false)注解将器lazy属性显示地设置为false。
1.3 自定义Banner
启动时打印的Banner可以通过在classpath中添加banner.txt文件或通过spring.banner.location属性设置为该文件的位置来自定义。如果该文件的编码不是UTF-8,你可以通过spring.banner.charset属性设置其字符编码。
在你的banner.txt文件中,你可以使用Enviroment中任何key,以及以下任何占位符。
变量 | 介绍 |
---|---|
| 你的应用程序的版本号,也就是 |
| 你的应用程序的版本号,如在`MANIFEST.MF`中声明的那样,并以格式化显示(用括号包围,以 |
| 你所使用的Spring Boot版本。 例如 |
| 你正在使用的Spring Boot版本,格式化显示(用大括号包围并以 |
| 其中 |
| 你的应用程序的标题,正如在 |
✦ 如果你想以编程方式生成一个Banner,可以使用SpringApplication.setBanner()方法。实现org.springframework.boot.Banner接口并实现自己的printBanner()方法。
你也可以使用spring.main.banner-mode属性来决定Beann打印模式。例如打印到System.out(console)上,发送到配置的logger。打印的Banner被注册为一个单例Bean,名字是springBootBanner。
✦ ${application.version}和${application.formatted-version}属性只有在你使用Springboot启动器时才可用。如果你正在运行一个未打包的jar,并使用java -cp <classpath> <mianclass>启动它,这些值将不会被解析。 这就是为什么我们建议总是使用【java org.springframework.boot.loader.JarLauncher】来启动未打包的jar。这将在构建classpath和启动你的应用程序之前初始化application.*的Banner变量。
1.4 自定义SpringApplication
如果SpringApplication的默认值不符合你的需求,你可以创建一个实例并对其进行自定义。例如要关闭Banner,你可以这样写。
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
1.5 Builder API
如果你需要建立一个包含层次结构的ApplicationContext(具有父/子关系多个context),或者你喜欢使用‘fluent’Builder API,你可以使用SpringApplicationBuilder。
SpringApplicationBuilder允许你链式调用多个方法,包括调用parent和child方法,创建一个层次结构,如下所示。
new SpringApplicationBuilder().sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
1.6 Application事件和监听器
除了常见的Spring框架,如ContextRefresheEvent外,SpringApplication还会发布一些额外的应用事件。
有些事件实际上是在ApplicationContext被创建之前触发的,所以你不能以@Bean的形式注册一个监听器。你可以通过SpringApplication.addListeners()方法或者SpringApplicationBuilder.listeners()方法注册它们。
如果你想让这些监听器自动注册,不管应用程序是如何创建的,你可以在你的项目添加一个META-INF/spring.factories文件,并通过ora.springframework.context.ApplicationListener属性来配置你的监听器,如下所示。
org.springframework.context.ApplicationListener=com.example.project.MyListener
当应用程序运行时,Application event按以下顺序发布:
- 一个ApplicationStartingEvent在运行开始时被发布,但在任何处理之前,除了注册监听器和初始化器之外。
- 当在上下文中使用Enviroment已知,但在创建上下文之前,将发布ApplicationEnvironmentPreparedEvent。
- 当ApplicationContext已准备好并且ApplicationContextInitializers被调用,但在任何Bean定义被加载之前ApplicationContextInitializedEvent被发布。
- 一个ApplicationprepareEvent将在刷新开始之前但在Bean定义加载后被发布。
- 在上下文被刷新之后,但在任何应用程序和命令行运行程序被调用之前,将发布一个ApplicationStartedEvent。
- 紧着这发布LivenessState.CORRECT状态的AvailabilityChangeEvent,表明应用程序被认为是存活的
- 在任何ApplicationRunner和CommandLineRunner被调用后,将发布一个ApplicationReadEvent
- 紧接着发布ReadinessState.ACCEPING_TRAFFIC状态的AvailabilityChangeEvent,表明应用程序已经准备好为请求提供服务。
- 如果启动时出现异常,将发布一个ApplicationFaildEvent。
以上列表仅包含与SpringApplication相关的SpringApplicationEvent。除此以外,以下事件也会在ApplicationPreparedEvent之后和ApplicationStartedEvent之前发布。
- 在WebServer准备好发布WebServerIniteializedEvent。ServletWebServerInitializedEvent和ReactiveWebServerInitializedEvent分布对应Servlet和reactive的实现。
- 当ApplicationContext被刷新时,将发布一个ContextRefreshedEvent。
✦ 你通常不需要使用application event,但直到它们的存在会很方便。在内部,Springboot使用事件来处理各种任务。
1.7 WEB环境(Environment)
SpringApplication会视图帮你创建正确类型的ApplicationContext。确定为WebApplicationType的算法如下。
- 如果Spring MVC存在,就会使用AnnotationConfigServletWebServerApplicationContext。
- 如果Spring MVC不存在而Spring WebFlux存在,则使用AnnotationConfigReactiveWebServerApplicationContext。
- 否则,将使用AnnotationConfigApplicationContext。
这意味着,如果你再同一个应用程序中使用Spring MVC和新的WebClient(来自于Spring WebFlux),Spring MVC将被默认使用。你可以通过调用setWebApplicationType(WebApplicationType)来轻松覆盖。
也可以通过调用 setApplicationContextFactory()来完全控制使用的ApplicationContext类型。
1.8 访问应用参数
如果你需要访问传递给SpringApplication.run()的命令行参数,你可以注入一个org.springframework.boot.ApplicationArguments bean。通过ApplicationArguments接口,你可以访问原始的String[]参数以及经过解析的option和non-option参数。如下所示:
import java.util.List;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs();
if (debug) {
System.out.println(files);
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
1.9 使用ApplicationRunner或CommandLineRunner
如果你需要在SpringApplication启动后运行一些特定的代码,你可以实现ApplicationRunner或者CommandLineRunner接口。这两个接口以相同的方式工作,并提供一个单一的run方法,该方法在SpringApplication.run()执行完毕之前被调用。
★ 这很适合我们用于执行那些需要在处理HTTP请求之前的任务。
CommandLineRunner接口以字符串数组形式提供了对应用程序参数的访问。而ApplicationRunner使用前面讨论的ApplicationArguments接口。下面是显示了一个带有run方法的CommandLineRunnerd的例子:
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// Do something...
}
}
如果定义了多个CommandLineRunner或ApplicationRunner Bean,并且需要他们按照特定的顺序先后执行。那么可以实现org.springframework.core.Ordered接口 或使用org.springframework.core.annotation.Order注解来指定顺序。