You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot应用启动两次问题咨询(含Servlet与Eh-Cache模块)

Spring Boot应用启动两次的解决方案

嘿,作为经常帮Spring Boot新手排查问题的开发者,你的这个启动两次的场景我太熟悉了!结合你给出的代码(继承了SpringBootServletInitializer,同时有@SpringBootApplication等注解),我来帮你分析原因和给出具体解决办法:

为什么会启动两次?

你当前的启动类同时承担了两个角色:

  • 作为内嵌容器启动入口:通过main方法调用SpringApplication.run()启动内嵌Tomcat/Jetty
  • 作为WAR部署入口:继承SpringBootServletInitializer,让外部Servlet容器(比如Tomcat)能加载Spring Boot应用

当你在IDE中直接运行启动类,或者打包部署时,如果两种启动逻辑被同时触发,就会导致应用初始化两次(你看到的日志会打印两次"Welcome to Application")。另外,@ServletComponentScan本身不会导致启动两次,但如果和Servlet容器的扫描逻辑叠加,可能会加重问题,但核心原因还是启动类的双重角色。

具体解决方案

1. 拆分启动类(最推荐的方案)

把内嵌容器启动和WAR部署的逻辑拆成两个独立的类,避免冲突:

主启动类(用于内嵌容器启动)

@EnableCaching
@SpringBootApplication
@ServletComponentScan
public class Application {
    private static final Logger log = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    public Application() {
        log.info("------------------------------");
        log.info(" Welcome to Application");
        log.info("------------------------------");
    }
}

独立的Servlet初始化类(仅用于WAR部署)

public class ServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        // 指向主启动类
        return application.sources(Application.class);
    }
}

这样拆分后:

  • 直接运行Application的main方法时,只会启动内嵌容器,不会触发SpringBootServletInitializer的逻辑
  • 打包成WAR部署到外部容器时,容器会加载ServletInitializer,启动Spring Boot应用,不会走main方法

2. 检查IDE运行配置

如果你是在IDE(比如IDEA/Eclipse)中运行,确保没有同时配置两种启动方式:

  • 不要同时运行Application的Java Application配置,又把WAR包部署到IDE内嵌的Tomcat中
  • 只选择一种启动方式:要么直接运行main方法(用内嵌容器),要么打包WAR部署到外部容器

3. 排除内嵌容器(如果仅做WAR部署)

如果你的应用只打算部署到外部Servlet容器,完全不需要内嵌容器,可以在依赖中排除内嵌Tomcat/Jetty,避免启动时的冲突:

Maven示例:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- 添加Servlet API依赖(编译时生效,容器运行时提供) -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>

Gradle示例:

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web') {
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
    }
    providedRuntime 'javax.servlet:javax.servlet-api:4.0.1'
}

验证方法

修改后启动应用,观察日志是否只打印一次"Welcome to Application",同时检查Servlet和EhCache的初始化是否正常,确保没有重复创建Bean的情况。

内容的提问来源于stack exchange,提问作者Boss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:25:32