Spring Mock MVC控制器通知异常处理测试失败问题咨询
我来帮你梳理下可能的问题点,这种情况我之前踩过好几次坑,大概率是以下某个细节没处理到位:
1. 先确认SecurityException的全类名是否完全匹配
这是最容易踩的坑!Java里有两个常见的SecurityException:
java.lang.SecurityException(JDK自带的基础异常类)org.springframework.security.core.SecurityException(Spring Security的核心异常类)
你要仔细核对:
- 控制器方法里抛出的实际异常类型,和
@ExceptionHandler(SecurityException.class)里指定的是不是同一个类? - 比如如果你的代码抛出的是Spring Security的
AuthenticationException(它是org.springframework.security.core.SecurityException的子类),理论上@ExceptionHandler(SecurityException.class)能覆盖子类,但前提是你指定的全类名和抛出的异常父类完全一致。
2. 检查异常抛出的时机是否在Controller处理流程内
Spring的@ControllerAdvice只能处理DispatcherServlet接管后的异常,也就是控制器方法内部、参数绑定、消息转换阶段抛出的异常。如果异常是在过滤器层面抛出的(比如Spring Security的Filter拦截请求时抛出的权限异常),那Controller Advice根本管不到——因为过滤器在DispatcherServlet之前执行,这时候的异常会被过滤器自身的异常处理机制处理,比如Spring Security的AuthenticationEntryPoint或者AccessDeniedHandler。
举个例子:如果你的接口加了@PreAuthorize注解,Spring Security的AOP代理在调用控制器方法前就抛出了AccessDeniedException,这时候这个异常不会走到Controller Advice,而是需要你配置Spring Security专属的异常处理器。
3. 验证MockMvc的构建方式是否合适
你用了standaloneSetup模式,这种模式是轻量级的,只初始化指定的控制器和Controller Advice,但如果你的控制器用到了AOP代理(比如Spring Security的注解、自定义AOP),standalone模式可能不会正确生成代理对象,导致异常抛出的逻辑和实际运行时不一致。
建议试试切换到webAppContextSetup模式,加载完整的Spring上下文来构建MockMvc,这样能保证所有Bean、AOP代理都正确初始化:
@Autowired private WebApplicationContext webApplicationContext; private MockMvc mockMvc; @BeforeEach void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) // 如果用到Spring Security,记得加上这个配置 .apply(springSecurity()) .build(); }
4. 确认Controller Advice的注解配置正确
虽然控制台已经打印了检测到@ExceptionHandler方法,但还是再确认下你的ExceptionHandlingControllerAdvice类上有没有加@RestControllerAdvice(返回JSON响应)或者@ControllerAdvice(返回视图)注解?如果只加了@Component,虽然可能被Spring扫描到,但不会被识别为Controller Advice(不过控制台既然检测到了,大概率是加了,但还是确认下更稳妥)。
5. 检查测试方法的写法是否正确
当MockMvc调用被Controller Advice处理的接口时,不会抛出异常,而是会返回处理后的HTTP响应。如果你的测试方法直接让异常传播出来,说明Controller Advice根本没触发。正确的测试写法应该验证响应状态码或响应体,比如:
@Test void testSecurityExceptionHandling() throws Exception { mockMvc.perform(get("/your-secure-endpoint")) .andExpect(status().isForbidden()) // 或者你自定义的状态码 .andExpect(content().json("{\"code\":403,\"message\":\"权限不足\"}")); // 验证自定义响应体 }
先从全类名匹配和异常抛出时机这两个点排查,这是最常见的原因,如果还是解决不了,可以再检查MockMvc的构建模式和测试写法。
内容的提问来源于stack exchange,提问作者FMC

