Spring Boot整合Spring Security使用数据库数据认证时出现Bean循环依赖创建失败问题
嘿,我看到你在整合Spring Boot和Spring Security、用数据库数据做用户认证的时候碰到了循环依赖导致Bean创建失败的问题,这个坑我之前也踩过,咱们一步步来解决它~
首先,这个BeanCurrentlyInCreationException的核心原因是Spring容器在创建Bean的过程中出现了闭环依赖:比如SecurityConfig需要某个Bean,而那个Bean又反过来需要SecurityConfig,导致Spring不知道该先初始化哪个Bean。在Security整合场景里,最常见的就是SecurityConfig和自定义UserDetailsService、DataSource或者AuthenticationManager之间形成了循环。
下面是几个实用的解决方法,你可以根据自己的代码情况尝试:
检查并调整依赖注入方式
很多时候循环依赖是因为构造注入的顺序问题。比如如果你的SecurityConfig通过构造函数注入了自定义UserDetailsService,而这个UserDetailsService又间接依赖了和Security相关的Bean(比如AuthenticationManager),就容易出问题。你可以试试把构造注入改成Setter注入,或者在构造注入时给依赖加上@Lazy注解,让Spring延迟初始化那个依赖Bean:@Configuration @EnableWebSecurity public class SecurityConfig { private UserDetailsService userDetailsService; // Setter注入替代构造注入 @Autowired public void setUserDetailsService(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } // 或者构造注入时加@Lazy // public SecurityConfig(@Lazy UserDetailsService userDetailsService) { // this.userDetailsService = userDetailsService; // } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .formLogin(Customizer.withDefaults()) .userDetailsService(userDetailsService); return http.build(); } }确保自定义UserDetailsService的独立性
你的自定义UserDetailsService应该只专注于从数据库查询用户数据,不要让它依赖SecurityConfig或者其他Security相关的Bean。比如如果需要操作数据库,直接注入JdbcTemplate或者DataSource就好,不要注入AuthenticationManager这类和Security配置绑定的Bean:@Service public class CustomUserDetailsService implements UserDetailsService { private final JdbcTemplate jdbcTemplate; // 只注入数据库操作相关的Bean public CustomUserDetailsService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 从数据库查询用户名、密码、权限的逻辑 String sql = "SELECT username, password, enabled FROM users WHERE username = ?"; User user = jdbcTemplate.queryForObject(sql, new Object[]{username}, (rs, rowNum) -> new User( rs.getString("username"), rs.getString("password"), rs.getBoolean("enabled"), true, true, true, AuthorityUtils.createAuthorityList("ROLE_USER") ) ); if (user == null) { throw new UsernameNotFoundException("User not found: " + username); } return user; } }避免直接注入AuthenticationManager到其他Bean
如果你需要使用AuthenticationManager,不要直接在其他Bean里注入它,而是通过AuthenticationConfiguration来获取,或者在SecurityConfig里把它定义为一个独立的Bean,这样可以避免循环:@Configuration @EnableWebSecurity public class SecurityConfig { // ...其他配置 @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } }排查不必要的依赖
仔细检查你的各个Bean的依赖关系,看看有没有不小心注入了不需要的Bean。比如有些同学会在UserDetailsService里注入SecurityConfig,这完全是多余的,直接去掉这类不必要的依赖就能解决循环问题。
按照上面的方法排查调整后,应该就能解决这个循环依赖的问题了。如果还是不行,可以把你的SecurityConfig和UserDetailsService的代码贴出来,咱们再进一步分析~
备注:内容来源于stack exchange,提问作者Bilolbek Rayimov

