普通main方法 springboot项目main方法

Q:普通main方法执行完之后程序就停止了 但是启动springboot项目为什么就没有停止

A:

核心原因:Spring Boot创建了非守护线程

1. main方法执行后发生了什么

java 复制代码
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        // 这里SpringApplication.run()会创建非守护线程
    }
}

2. SpringApplication.run()的工作原理

java 复制代码
// Spring Boot源码简化示意
public ConfigurableApplicationContext run(String... args) {
    // 1. 准备环境
    // 2. 创建应用上下文
    // 3. 刷新上下文(关键步骤)
    refreshContext(context);
    // 4. 执行Runner
    // 5. 返回上下文(但此时main线程已完成,程序继续运行)
}

3. 核心机制:嵌入式Web服务器创建非守护线程

Spring Boot启动时会根据依赖创建相应的Web服务器:

java 复制代码
// Tomcat为例
public class TomcatWebServer {
    public void start() throws WebServerException {
        // Tomcat会启动线程池,这些线程都是非守护线程
        this.tomcat.start();
        // 启动一个非守护线程来保持服务器运行
        startDaemonAwaitThread();
    }
    
    private void startDaemonAwaitThread() {
        Thread awaitThread = new Thread("container-" + (containerCounter.get())) {
            @Override
            public void run() {
                // 这个线程会一直阻塞,直到服务器关闭
                TomcatWebServer.this.tomcat.getServer().await();
            }
        };
        awaitThread.setDaemon(false); // 非守护线程!
        awaitThread.start();
    }
}

4. Java线程的守护/非守护机制

java 复制代码
public class ThreadExample {
    public static void main(String[] args) {
        // 守护线程:主线程结束后会自动结束
        Thread daemonThread = new Thread(() -> {
            while (true) {
                System.out.println("Daemon running...");
            }
        });
        daemonThread.setDaemon(true);
        daemonThread.start();
        
        // 程序会立即结束,因为只有守护线程
    }
}

5. Spring Boot保持运行的详细流程

text 复制代码
Spring Boot启动流程:
1. main线程调用 SpringApplication.run()
2. 创建Spring容器和应用上下文
3. 根据类路径检测到Web依赖(如spring-boot-starter-web)
4. 启动嵌入式Web服务器(Tomcat/Netty/Jetty)
5. Web服务器创建并启动非守护线程:
   - 监听端口(如8080)
   - 处理HTTP请求
   - 保持连接
6. main线程执行完毕,但JVM检测到还有非守护线程在运行
7. JVM保持运行,直到所有非守护线程结束

6. 验证:查看Spring Boot的线程

java 复制代码
// 可以在Spring Boot启动后查看线程
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        
        // 查看所有线程
        Thread.getAllStackTraces().keySet().forEach(thread -> {
            System.out.println(thread.getName() + " - 是否守护线程: " + thread.isDaemon());
        });
    }
}

输出示例:

复制代码
main - 是否守护线程: false
Tomcat-startStop-1 - 是否守护线程: false
http-nio-8080-Acceptor - 是否守护线程: false
http-nio-8080-ClientPoller - 是否守护线程: false
...

7. 如何优雅关闭Spring Boot应用

java 复制代码
// 方式1:调用close()方法
context.close();

// 方式2:发送关闭信号(如Ctrl+C)
// Spring Boot会注册JVM关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    context.close();
}));

// 方式3:通过Actuator端点
// POST /actuator/shutdown

总结

Spring Boot项目在main方法执行完后不停止的原因是:

  1. Web服务器创建了非守护线程来监听端口和处理请求
  2. JVM规则:只要还有非守护线程在运行,JVM就不会退出
  3. 事件驱动模型:Web服务器在等待HTTP请求,处于阻塞状态
  4. 优雅的生命周期管理:需要显式关闭或接收关闭信号才会停止

这确保了Spring Boot应用可以作为独立的服务器进程长期运行。

相关推荐
无敌最俊朗@5 小时前
STL-vector面试剖析(面试复习4)
java·面试·职场和发展
追逐时光者5 小时前
一款开源、现代化的 WinForm UI 控件库
后端·.net
PPPPickup5 小时前
easychat项目复盘---获取联系人列表,联系人详细,删除拉黑联系人
java·前端·javascript
LiamTuc6 小时前
Java构造函数
java·开发语言
长安er6 小时前
LeetCode 206/92/25 链表翻转问题-“盒子-标签-纸条模型”
java·数据结构·算法·leetcode·链表·链表翻转
菜鸟plus+6 小时前
N+1查询
java·服务器·数据库
我要添砖java6 小时前
《JAVAEE》网络编程-什么是网络?
java·网络·java-ee
CoderYanger6 小时前
动态规划算法-01背包问题:50.分割等和子集
java·算法·leetcode·动态规划·1024程序员节
花月C6 小时前
个性化推荐:基于用户的协同过滤算法
开发语言·后端·算法·近邻算法
cci7 小时前
还在用conda?,试试uv,提高包的安装速度
后端