【バグ記録】Spring Securityの設定クラスで循環参照エラーが発生する場合の対処方法
現象
Spring Boot 2.6にアップデートした際、アプリ起動時にSecurityConfigで下記のエラーが発生する。
Description: The dependencies of some of the beans in the application context form a cycle:
┌──->──┐
| securityConfig
└──<-──┘
原因
SecurityConfigにおいて、configureGlobalメソッドで使用する自身のBeanを@Autowiredしようとしていることが原因で循環参照が発生している。
※Spring Boot 2.6以上では循環参照がデフォルトで禁止されている。
(SecurityConfig.java)
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 省略 } @Autowired // !!ここが原因!! public void configureGlobal( AuthenticationManagerBuilder auth, UserDetailsServiceImpl userDetailsService, PasswordEncoder passwordEncoder) throws Exception { auth.eraseCredentials(true).userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
解決方法
① configureGlobalを別クラスにする
(SecurityConfig.java)
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 省略 } @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
(GlobalConfiguration.java)
@Configuration @EnableWebSecurity public class GlobalConfiguration { @Autowired public void configureGlobal( AuthenticationManagerBuilder auth, UserDetailsServiceImpl userDetailsService, PasswordEncoder passwordEncoder) throws Exception { auth.eraseCredentials(true).userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } }
② @Lazyアノテーションを付与する
@Lazy
アノテーションで遅延初期化をさせます。
@Lazy
が付与された@Bean
または @Component
は、別の Bean によって参照されるか、それを囲む BeanFactory から明示的に取得されるまで初期化されません。
リファレンス: Lazy (Spring Framework API) - Javadoc
(SecurityConfig.java)
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 省略 } @Autowired @Lazy // 追加 public void configureGlobal( AuthenticationManagerBuilder auth, UserDetailsServiceImpl userDetailsService, PasswordEncoder passwordEncoder) throws Exception { auth.eraseCredentials(true).userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }