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

Spring Boot打包JAR运行报错:resourceHandlerMapping创建失败(无ServletContext)

问题描述

Spring Boot应用在IntelliJ中运行正常,但构建JAR文件运行(包括Docker化后)时出现如下错误:

projekt-backend-1   | 16:06:15.945 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
projekt-backend-1   | org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
projekt-backend-1   |   at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658)
projekt-backend-1   |   at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638)
projekt-backend-1   |   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352)
projekt-backend-1   |   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195)
projekt-backend-1   |   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
projekt-backend-1   |   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
projekt-backend-1   |   at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
projekt-backend-1   |   at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
projekt-backend-1   |   at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
projekt-backend-1   |   at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
projekt-backend-1   |   at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
projekt-backend-1   |   at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
projekt-backend-1   |   at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
projekt-backend-1   |   at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
projekt-backend-1   |   at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
projekt-backend-1   |   at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
projekt-backend-1   |   at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
projekt-backend-1   |   at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
projekt-backend-1   |   at com.hdmstuttgart.mi.backend.BackendApplication.main(BackendApplication.java:23)
projekt-backend-1   | Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
projekt-backend-1   |   at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
projekt-backend-1   |   at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
projekt-backend-1   |   ... 18 common frames omitted
projekt-backend-1   | Caused by: java.lang.IllegalStateException: No ServletContext set
projekt-backend-1   |   at org.springframework.util.Assert.state(Assert.java:76)
projekt-backend-1   |   at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.resourceHandlerMapping(WebMvcConfigurationSupport.java:591)
projekt-backend-1   |   at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
projekt-backend-1   |   at java.base/java.lang.reflect.Method.invoke(Method.java:577)
projekt-backend-1   |   at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
projekt-backend-1   |   ... 19 common frames omitted
projekt-backend-1 exited with code 1

相关配置文件如下:

ApplicationConfig

@Configuration
@RequiredArgsConstructor
@ComponentScan(basePackages = "com.hdmstuttgart.mi.backend")
public class ApplicationConfig {

    private final UserRepository userRepository;

    @Bean
    public JavaMailSender javaMailSender() {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost("smtp.gmail.com");
        mailSender.setPort(587);

        mailSender.setUsername("${mailCredentials.username");
        mailSender.setPassword("${mailCredentials.password");

        Properties props = mailSender.getJavaMailProperties();
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.debug", "true");

        return mailSender;
    }

    @Bean
    public MJMLClient mjmlClient() {
        String appId = "${mjmlSecrets.appId}";
        String mailUsername = "${mailCredentials.username");
        return MJMLClient.newDefaultClient()
                .withApplicationID(appId)
                .withApplicationKey(mailUsername);
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return username -> userRepository.findByEmail(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService());
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public ModelMapper modelMapper() {
        return new ModelMapper();
    }
}

WebConfig

@Configuration
@EnableWebMvc
@WebAppConfiguration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .exposedHeaders("Authorization")
                /*.allowCredentials(true)*/
                .maxAge(3600L);
    }
}

主类

@SpringBootApplication(exclude = {WebMvcAutoConfiguration.class })
public class BackendApplication {

    private static final Logger log = LoggerFactory.getLogger(BackendApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(BackendApplication.class, args);
        log.debug("Backend application started");
    }

}

application.yml

.env:
  prefix: ""

mailCredentials:
  username: ${MAIL_USERNAME}
  password: ${MAIL_PASSWORD}

mjmlSecrets:
  appId: ${MJML_APP_ID}
  appKey: ${MJML_APP_KEY}

spring:

  datasource:
    url: jdbc:postgresql://localhost:5432/openbarber
    username: postgres
    password: admin

  jpa:
    show-sql: true  # Log each SQL query
    hibernate:
      ddl-auto: update  # DDL auto (schema change strategy)
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect

  mail:
    host: smtp.gmail.com
    port: 587
    username: ${MAIL_USERNAME}
    password: ${MAIL_PASSWORD}
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
            required: true

#  hateoas:
#    use-hal-as-default-json-media-type: false

Dockerfile

FROM openjdk:latest

WORKDIR /app

COPY . .

RUN ./mvnw dependency:go-offline -B

RUN ./mvnw package -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)

COPY target/Backend.jar .

CMD ["java", "-jar", "Backend.jar"]
解决思路

1. 移除主类中对WebMvcAutoConfiguration的排除

主类上的@SpringBootApplication(exclude = {WebMvcAutoConfiguration.class })是核心问题。Spring Boot的自动配置会自动初始化ServletContext,排除这个类后,Web MVC的自动配置逻辑失效,导致resourceHandlerMapping创建时找不到ServletContext。

修改后主类:

@SpringBootApplication
public class BackendApplication {

    private static final Logger log = LoggerFactory.getLogger(BackendApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(BackendApplication.class, args);
        log.debug("Backend application started");
    }

}

2. 简化WebConfig配置

@EnableWebMvc会完全接管Spring MVC的配置,关闭自动配置。如果仅需配置CORS,不需要添加@EnableWebMvc和@WebAppConfiguration,直接实现WebMvcConfigurer即可,Spring Boot的自动配置依然生效:

修改后WebConfig:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .exposedHeaders("Authorization")
                /*.allowCredentials(true)*/
                .maxAge(3600L);
    }
}

3. 修复配置文件中的语法错误

ApplicationConfig的javaMailSender和mjmlClient方法中存在配置占位符语法错误,缺少闭合的},本地运行可能因IDE补全正常,但打包成JAR后会加载失败:

  • mailSender.setUsername("${mailCredentials.username"); → 改为"${mailCredentials.username}"
  • mailSender.setPassword("${mailCredentials.password"); → 改为"${mailCredentials.password}"
  • String mailUsername = "${mailCredentials.username"); → 改为"${mailCredentials.username}"

另外,application.yml已配置Spring Mail参数,ApplicationConfig中的javaMailSender Bean可直接移除,复用Spring Boot自动配置的实例,避免重复配置。

4. 优化Docker构建流程

当前Dockerfile存在冗余步骤,且复制整个项目目录会增大镜像体积,优化后:

FROM openjdk:latest

WORKDIR /app

# 先复制依赖配置,利用Docker缓存
COPY pom.xml ./
COPY ./mvnw ./
COPY ./.mvn ./.mvn

RUN ./mvnw dependency:go-offline -B

# 复制源代码
COPY src ./src

# 打包
RUN ./mvnw package -DskipTests

# 复制最终JAR
COPY target/Backend.jar ./Backend.jar

CMD ["java", "-jar", "Backend.jar"]

同时注意:Docker内部无法通过localhost访问主机PostgreSQL,需将application.yml中的数据库地址修改为主机IP,或使用Docker Compose的服务名(若用Compose编排)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:32:02