Spring 5迁移至Spring 6后WebMvc Controller方法陷入无限循环
问题背景
从Spring 5迁移至Spring 6后,出现WebMvc控制器方法被无限调用的异常,相同配置在Spring 5环境下可正常运行。相关配置代码如下:
SecurityWebApplicationInitializer.java
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { }
WebMvcApplicationInitializer.java
public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getServletConfigClasses() { return new Class[] { SecurityConfig.class, WebMvcConfig.class }; } @Override protected Class<?>[] getRootConfigClasses() { return null; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
WebMvcConfig.java
@Configuration @EnableWebMvc @ComponentScan(basePackages = { "com.feedbager" }) public class WebMvcConfig implements WebMvcConfigurer { @Bean public InternalResourceViewResolver resolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setViewClass(JstlView.class); resolver.setPrefix("/WEB-INF/jsp/"); resolver.setSuffix(".jsp"); return resolver; } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler("/resources/**") .addResourceLocations("/resources/"); } }
SecurityConfig.java
@Configuration @EnableWebSecurity @ComponentScan("com.feedbager") public class SecurityConfig { @Bean public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); authenticationManagerBuilder.authenticationProvider(authenticationProvider); return authenticationManagerBuilder.build(); } @Bean public WebSecurityCustomizer webSecurityCustomizer() { return web -> web.ignoring() .requestMatchers("/resources/**"); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry .requestMatchers("/shop/**").hasAnyAuthority("ROLE_SHOP") .requestMatchers("/user/**").hasAnyAuthority("ROLE_USER") .requestMatchers("/manufacturer/**").hasAnyAuthority("ROLE_MANUFACTURER") .requestMatchers("/register").permitAll() .requestMatchers("/index").permitAll() .requestMatchers("/accessDenied").permitAll() .requestMatchers("/verify").permitAll() .requestMatchers("/shops").permitAll() .requestMatchers("/manufacturers").permitAll() .requestMatchers("/articles").permitAll() .anyRequest().authenticated()) .formLogin(form -> form .successHandler(loginSuccessHandler).loginProcessingUrl("/process-login").loginPage("/index").failureUrl("/index?error") .permitAll() ) .httpBasic(Customizer.withDefaults()) .sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); return http.build(); } @Bean public PasswordEncoder passwordEncoder(){ PasswordEncoder encoder = new BCryptPasswordEncoder(); return encoder; } @Autowired private AuthenticationProvider authenticationProvider; @Autowired private LoginSuccessHandler loginSuccessHandler; }
疑问:Spring 6在控制器返回逻辑方面是否有新变化?如何让控制器方法仅被调用一次?
问题分析与解决方案
核心原因
你的问题并非Spring 6控制器返回逻辑的直接变更导致,而是重复组件扫描与无状态会话配置冲突在Spring 6更严格的Bean生命周期管理下暴露的问题:
重复Bean实例化
WebMvcConfig和SecurityConfig都配置了@ComponentScan("com.feedbager"),且两者均被注册到Servlet上下文(getServletConfigClasses返回这两个配置类)。这会导致控制器、处理器等Bean被重复创建,请求分发时出现循环调用。Spring 5对重复Bean的处理更宽松,未触发该问题,但Spring 6严格了Bean的唯一性校验与生命周期管理。无状态会话与表单登录的矛盾
你在Security配置中设置了SessionCreationPolicy.STATELESS,但表单登录依赖会话存储认证状态。无状态配置会导致每次请求都需要重新认证,进而触发控制器方法的重复调用。
修复步骤
拆分组件扫描范围
移除SecurityConfig上的@ComponentScan("com.feedbager"),仅保留WebMvcConfig的组件扫描,或对两者的扫描范围做细分(比如com.feedbager.controller给WebMvc,com.feedbager.security给Security),避免Bean重复创建:@Configuration @EnableWebSecurity // 移除@ComponentScan("com.feedbager") public class SecurityConfig { // 原有代码保持不变 }调整会话策略
表单登录依赖会话维护认证状态,需将会话策略改为默认的SessionCreationPolicy.IF_REQUIRED:.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) )优化上下文配置
将Security配置移至根上下文,WebMvc配置保留在Servlet上下文,避免上下文混淆:@Override protected Class<?>[] getRootConfigClasses() { return new Class[] { SecurityConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[] { WebMvcConfig.class }; }
关于Spring 6控制器返回逻辑的变化
Spring 6在控制器返回逻辑上的主要变更集中在:
- 默认使用
PathPatternParser替代AntPathMatcher(可通过配置切换回旧模式) - 响应式控制器的行为优化
- 对
@ResponseBody和视图返回的类型推断更严格
但这些变化与你的无限调用问题无关,无需针对返回逻辑做额外调整。
内容的提问来源于stack exchange,提问作者Helge1977

