在写一个 SpringBoot
引入 Mybatis Plus
的 Demo
,在启动服务时出现了问题,服务启动失败并且没有打印报错信息。
解决问题 控制台打印的信息如下
搜寻解决方案,看到说可以在启动类上捕获异常,如下:
1 2 3 4 5 try { SpringApplication.run(MybatisPlusDemoApplication.class, args); } catch (Exception e) { throw new RuntimeException(e); }
这样做并没有效果,然后以为是引入的 SpringBoot
版本与 Mybatis-Plus
版本不兼容导致的,尝试降低 SpringBoot
的版本,还是启动不起来。
在 springboot项目启动不报错,但一启动就断开连接问题排查实录 这篇文章中找到了解决办法,在 pom.xml
添加 spring-boot-starter-web
依赖,之后就能成功启动了。
问题分析 这个项目不是 web
项目,因此我想只需要引入 spring-boot-starter
依赖就可以了,因为我想的是 jar
的依赖越少越好,只需要依赖必要的包,非必要的 jar
没必要引入,但实际是只加这个依赖项目启动不起来。那么为何加了 spring-boot-starter-web
依赖后,项目就能启动成功了?
SpringBoot
在启动时会进行一个 Web
类型推断 操作,我们看下 SpringApplication
的构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 public SpringApplication (Class<?>... primarySources) { this (null , primarySources); } public SpringApplication (ResourceLoader resourceLoader, Class<?>... primarySources) { this .resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null" ); this .primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this .webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this .mainApplicationClass = deduceMainApplicationClass(); }
Web
类型推断的逻辑在 WebApplicationType.deduceFromClasspath()
中进行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 NONE, SERVLET, REACTIVE; private static final String[] SERVLET_INDICATOR_CLASSES = { "jakarta.servlet.Servlet" , "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet" ;private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler" ;private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer" ;static WebApplicationType deduceFromClasspath () { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null ) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null ) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null )) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null )) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
可以看到,Spring Boot
支持3种类型的服务:
None
。非 Web
应用,不需要启动内置 Web
服务器。
Servlet
。基于 Servlet
的 Web
应用,需要启动内置 Servlet Web
服务器。
Reactive
。基于 Reactive
的 Web
应用,需要启动内置 Reactive Web
服务器。
可以通过 Spring Initializr
网站来尝试下构建这三种类型的项目:https://start.spring.io
None Web
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency >
Servlet Web
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency >
Reactive Web
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-webflux</artifactId > </dependency >
回到上面的问题,项目启动失败并且没有打印错误日志,就是因为启动了一个 None Web
服务(只引入了 spring-boot-starter
依赖)。在 None Web
类型下,应用启动运行后就自动关闭了,并不会启动内置的 web
服务器,也不会监听任何端口。
引入 spring-boot-starter-web
依赖后,Spring Boot
就会启动一个 Servlet Web
类型的服务,因此就正常了。
补充:spring-boot-starter-web
包含的依赖项 我们再深入看一下 spring-boot-starter-web
引入了哪些依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > <version > 3.3.1</version > <scope > compile</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-json</artifactId > <version > 3.3.1</version > <scope > compile</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-tomcat</artifactId > <version > 3.3.1</version > <scope > compile</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-web</artifactId > <version > 6.1.10</version > <scope > compile</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 6.1.10</version > <scope > compile</scope > </dependency > </dependencies >
下面是 spring-boot-starter-web
依赖树的一个简化示例,展示了主要依赖及其子依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 spring-boot-starter-web ├── spring-boot-starter │ ├── spring-core │ ├── spring-context │ ├── spring-aop │ ├── slf4j-api │ └── logback-classic ├── spring-boot-starter-json │ ├── jackson-databind │ ├── jackson-core │ ├── jackson-annotations │ ├── jackson-datatype-jdk8 │ └── jackson-datatype-jsr310 ├── spring-boot-starter-tomcat │ ├── tomcat-embed-core │ ├── tomcat-embed-el │ └── tomcat-embed-websocket ├── spring-web │ ├── spring-beans │ ├── spring-web │ └── spring-webmvc └── spring-webmvc ├── spring-beans ├── spring-context ├── spring-core └── spring-web
作为对比,我们再看下 spring-boot-starter-webflux
引入的依赖项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > <version > 3.3.1</version > <scope > compile</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-json</artifactId > <version > 3.3.1</version > <scope > compile</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-reactor-netty</artifactId > <version > 3.3.1</version > <scope > compile</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-web</artifactId > <version > 6.1.10</version > <scope > compile</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webflux</artifactId > <version > 6.1.10</version > <scope > compile</scope > </dependency > </dependencies >
下面是 spring-boot-starter-webflux
的依赖树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 spring-boot-starter-webflux ├── spring-boot-starter │ ├── spring-core │ ├── spring-context │ ├── spring-aop │ ├── slf4j-api │ └── logback-classic ├── spring-boot-starter-json │ ├── jackson-databind │ ├── jackson-core │ ├── jackson-annotations │ ├── jackson-datatype-jdk8 │ └── jackson-datatype-jsr310 ├── spring-boot-starter-reactor-netty │ ├── reactor-netty-core │ └── reactor-netty-http ├── spring-web │ ├── spring-beans │ ├── spring-context │ └── spring-web └── spring-webflux ├── spring-beans ├── spring-context ├── reactor-core ├── reactor-netty ├── spring-web └── spring-webflux
总结 本文给出了 [SpringBoot启动失败但没有打印报错信息] 问题的处理办法,通过分析源码得知问题是由 Spring Boot
的 Web
类型推断引起的,由于没有引入 spring-boot-starter-web
依赖,导致启动了一个 none web
服务。
之后又了解了 servlet web
对应的 spring-boot-starter-web
和 reactive web
对应的 spring-boot-starter-webflux
包含的依赖项。
对于这些日常开发中经常使用的依赖,它们引入了哪些依赖最好还是要做到心里有数,这样出现问题也能更好的定位。
参考文章 springboot项目启动不报错,但一启动就断开连接问题排查实录
WebApplicationType
SpringBoot源码学习(一)—启动流程