普通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 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp6 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑6 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯7 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan9 小时前
多Agent之间的区别
后端
青石路11 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充11 小时前
1.面向对象设计思想
后端
IT_陈寒12 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro12 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗12 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端