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

如何让Spring MVC仅创建一个Spring上下文?消除多上下文导致的Bean重复问题

解决Spring MVC双上下文导致Bean重复、异步线程翻倍的问题

这种情况我之前帮不少开发者排查过,确实挺头疼的——双倍的Bean实例和异步线程不仅会白白消耗服务器资源,还可能引发各种诡异的并发问题(比如重复执行定时任务、数据不一致等)。结合你的场景,核心问题是Spring MVC的根WebApplicationContext和DispatcherServlet的WebApplicationContext加载了重复的Bean定义,导致所有组件都被实例化了两次。下面我给你拆解常见原因和对应的解决办法:

一、最常见的坑:配置文件重复加载

Spring MVC中,ContextLoaderListener负责初始化根上下文(全局共享的上下文),而DispatcherServlet会初始化自己的MVC专用上下文(是根上下文的子上下文)。如果两者的contextConfigLocation指向了同一个/重叠的XML配置文件,就会导致所有Bean被加载两次。

错误示例(web.xml)

<!-- 根上下文加载applicationContext.xml -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<!-- DispatcherServlet又加载了同一个applicationContext.xml -->
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </init-param>
</servlet>

解决办法:拆分配置文件

  • 根上下文:负责加载业务逻辑、数据源、异步任务、定时任务等全局Bean(比如applicationContext.xml里放<context:component-scan base-package="com.example.service,com.example.repository"/>、数据源配置、@Async相关的@EnableAsync等)。
  • MVC上下文:只加载Web层专属Bean(比如新建spring-mvc.xml,里面放<context:component-scan base-package="com.example.controller"/>、视图解析器、拦截器、@EnableWebMvc等)。

调整后的web.xml示例:

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-mvc.xml</param-value>
    </init-param>
</servlet>

二、注解扫描范围重叠(基于JavaConfig的场景)

如果你的项目用Java配置类代替XML,很容易犯的错误是根上下文和MVC上下文的@ComponentScan范围重复,导致所有@Service、@Component、@Async标注的Bean被实例化两次。

错误示例

// 根上下文配置类(被ContextLoaderListener加载)
@Configuration
@ComponentScan("com.example") // 扫描整个项目包,包含Controller和Service
public class RootConfig {}

// MVC配置类(被DispatcherServlet加载)
@Configuration
@EnableWebMvc
@ComponentScan("com.example") // 再次扫描整个包,重复实例化Bean
public class WebMvcConfig {}

解决办法:精准拆分扫描范围

两种方式任选其一:

  1. 按包拆分:根上下文扫业务层,MVC上下文只扫控制器层
// 根上下文:扫Service、Repository等
@Configuration
@ComponentScan(basePackages = {"com.example.service", "com.example.repository"})
@EnableAsync // 异步任务配置放在根上下文
public class RootConfig {}

// MVC上下文:只扫Controller
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.controller")
public class WebMvcConfig {}
  1. 用过滤规则拆分:根上下文排除@Controller,MVC上下文只包含@Controller
// 根上下文:排除Controller注解的类
@Configuration
@ComponentScan(basePackages = "com.example", 
               excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class))
@EnableAsync
public class RootConfig {}

// MVC上下文:只包含Controller注解的类,关闭默认扫描规则
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example",
               includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
               useDefaultFilters = false)
public class WebMvcConfig {}

三、代码中手动创建了额外的ApplicationContext

还有一种可能:你在业务代码里手动调用了new ClassPathXmlApplicationContext(...)或者AnnotationConfigApplicationContext(...),导致额外的Spring上下文被初始化,和容器本身的上下文重复。

排查办法

全局搜索代码中所有创建ApplicationContext实例的地方,确保只有Spring的ContextLoaderListener和DispatcherServlet负责上下文初始化,不要手动创建上下文实例。

验证问题是否解决

可以通过以下方式确认:

  • 在关键Bean的构造方法中添加日志(比如System.out.println("Bean实例化:" + this.hashCode())),启动应用后看是否打印两次相同Bean的日志。
  • 用JVM监控工具(比如VisualVM、JConsole)查看Bean实例的数量,确认是否恢复到预期的数量。
  • 查看异步线程池的线程数量,确认不再翻倍。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:16:31