环境准备
- idea 2020.1.2
- maven 3.6.3
- jdk 1.8
- 初始化springboot 项目demo
场景描述
- 场景描述:很多人在学习,或者搭建springboot 项目的时候, 或多或少都会遇到项目 项目点击启动, 没有任何报错,就挂掉了, 例如:
- 怎么就解决呢? 如果不了解springboot的, 肯定会百度,百度大多解决办法如下:
添加web依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 按照第二步添加web依赖:
在尝试启动:
启动成功, 度娘还是很管用, 问题解决了…
现实思考
既然添加spring-boot-starter-web 能够解决问题, 那我们肯定也想知道为什么项目启动需要我们添加spring-boot-starter-web呢? 不添加为什么会 挂掉呢? 接下来我们一起来看看springboot项目启动的过程, 找到我们懵逼的原因在哪…
找出原因
- 我们从springboot 启动找出 不添加web依赖 项目自动挂掉的原因
// 项目启动
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
// SpringBootApplication 类run方法的实现里面有一个刷新上下文的方法refreshContext()调用
// 在refreshContext()方法里面
private void refreshContext(ConfigurableApplicationContext context) {
// 刷新应用上下文
refresh((ApplicationContext) context);
// 判断是否给应用上下文注册或者关机的钩子操作 默认true
if (this.registerShutdownHook) {
try {
// 给应用上下文注册钩子
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
// 钩子:向JVM运行时注册一个关闭钩子,在JVM关闭时关闭此上下文,除非此时它已经关闭
@Override
public void registerShutdownHook() {
// 判断关闭钩子是否注册
if (this.shutdownHook == null) {
// 尚未注册, 执行
this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
@Override
public void run() {
synchronized (startupShutdownMonitor) {
doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook); // 添加关闭注册钩子
}
}
// 到这相信大家会看出点问题, 主要就是startupShutdownMonitor监视器 是否执行doClose()方法, 当我们不引入web依赖时, startupShutdownMonitor 会监视并执行doClose(), 导致系统无报错挂掉
// 那为什么呢
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 为servlet, 系统会给他加载内嵌服务器, 但是当我们 不引入时,webApplicationType 为 NONE
- 通过对代码的追朔, 在我们不引入web依赖, 也不指定项目所属的服务类型例如SERVLET, 在执run方法时就不能创建对应的服务类型, 导致连接vm失败, 项目挂掉, 所以如果我们不引用web依赖, 就指定其他服务类型
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
// 去掉tomcat
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
// 加入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>