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

拆分@ComponentScan与@EnableWebMvc至不同配置类引发请求映射失效问题

问题

我定义了两个Spring配置类:

@Configuration
@EnableWebMvc
public class WebConfig {
}

@Configuration
@ComponentScan("io.abned.spring.websocket.controllers")
public class AppConfig {
}

同时在io.abned.spring.websocket.controllers包下有控制器类及对应的测试类:

@RestController
@RequestMapping("api/v1")
public class HelloController {

    @GetMapping("hello")
    public String hello() {
        return "Hello, world!";
    }
}

@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextHierarchy({
        @ContextConfiguration(classes = AppConfig.class),
        @ContextConfiguration(classes = WebConfig.class)
})
class HelloControllerIntegrationTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @BeforeEach
    void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    void testHello() throws Exception {
        mockMvc.perform(get("/api/v1/hello"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$").value("Hello, world!"));
    }
}

使用Spring 6.1.10(未使用Spring Boot)时,上述测试执行失败,但将@ComponentScan移至WebConfig后测试成功。请问为何拆分@ComponentScan与@EnableWebMvc到不同配置类时会出现此问题?

原因分析

问题核心在于Spring上下文层级与请求映射处理器的扫描范围限制:

  1. 通过@ContextHierarchy定义的两层上下文存在父子关系:AppConfig是父上下文,WebConfig是子Web上下文(因标注了@WebAppConfiguration)。
  2. @EnableWebMvc会自动注册RequestMappingHandlerMapping组件,它的职责是扫描并注册控制器的请求映射规则。
  3. 默认情况下,RequestMappingHandlerMapping的detectHandlerMethodsInAncestorContexts属性为false,仅会扫描当前所在的子Web上下文中的控制器Bean。
  4. 你的HelloController是由父上下文AppConfig的@ComponentScan扫描注册的,不在子Web上下文的扫描范围内,因此请求映射无法被识别,测试时会返回404导致失败。
  5. 若将@ComponentScan移至WebConfig,控制器会被注册到子Web上下文中,自然能被RequestMappingHandlerMapping检测到,测试也就正常通过了。
解决方案

修改WebConfig,让它直接继承DelegatingWebMvcConfiguration并覆写createRequestMappingHandlerMapping方法,开启对祖先上下文的控制器扫描:

@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {
    @Override
    protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
        RequestMappingHandlerMapping requestMapping = super.createRequestMappingHandlerMapping();
        requestMapping.setDetectHandlerMethodsInAncestorContexts(true);
        return requestMapping;
    }
}

配置完成后,RequestMappingHandlerMapping会扫描当前上下文及其所有父上下文中的控制器Bean,就能正确识别到父上下文里的HelloController了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:32:34