SpringBoot中WebSecurityConfigurerAdapter与UserDetailsService循环依赖问题
问题根源
该循环依赖属于典型的Spring Bean依赖链闭环问题,触发逻辑如下:
WebSecurityConfig作为安全配置类,配置认证管理器时需注入UserDetailsService的实现类ApplicationUserServiceImplApplicationUserServiceImpl中注入了在WebSecurityConfig内部声明的PasswordEncoderBean- 最终形成
WebSecurityConfig→ApplicationUserServiceImpl→PasswordEncoder(归属于WebSecurityConfig的内部Bean)的依赖闭环,导致ApplicationContext加载失败。
解决方案
方案1:独立声明PasswordEncoder Bean(最推荐)
不要把PasswordEncoder的Bean定义放在WebSecurityConfig内部,单独抽为全局配置类切断依赖链,示例代码:
// 新增独立配置类,单独存放通用安全相关Bean @Configuration public class SecurityBeanConfig { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
之后删除WebSecurityConfig里的PasswordEncoder Bean定义即可,此时依赖链变为SecurityBeanConfig → PasswordEncoder → ApplicationUserServiceImpl → WebSecurityConfig,闭环被直接打破。
方案2:使用@Lazy注解延迟加载依赖
如果不想调整现有配置类结构,可以在ApplicationUserServiceImpl注入PasswordEncoder时添加@Lazy注解,延后Bean的初始化时机:
@Service public class ApplicationUserServiceImpl implements UserDetailsService { // 加@Lazy延迟注入,避免容器启动初始化阶段触发依赖闭环 @Autowired @Lazy private PasswordEncoder passwordEncoder; // 其余业务代码保持不变 }
该方案会在第一次调用PasswordEncoder实例时才完成注入,不会在容器启动阶段触发循环依赖校验。
方案3:直接在UserDetailsService实现类中初始化PasswordEncoder
如果你的PasswordEncoder不需要全局复用,可以直接在ApplicationUserServiceImpl内部初始化实例,无需依赖容器注入:
@Service public class ApplicationUserServiceImpl implements UserDetailsService { // 直接内部初始化实例,不依赖容器注入的Bean private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); // 其余业务代码保持不变 }
如果其他业务模块也需要用到PasswordEncoder,不推荐使用该方案。
补充注意事项
Spring Security 5.7及以上版本已经废弃WebSecurityConfigurerAdapter,如果是新项目可以直接使用组件式安全配置方式,也能从根源上避免这类配置类的循环依赖问题。
内容的提问来源于stack exchange,提问作者Juan Ignacio López
相关产品推荐
相关产品推荐

