The dependencies of some of the beans in the application context form a cycleが解決できない
解決したいこと
The dependencies of some of the beans in the application context form a cycleというspring bootのエラーを解決したいです。
現在Spring bootを学習している者です。
Spring解体新書という参考書を学習中、下記のようなエラー起きてしまい、
つまづいてしまいました。
発生している問題・エラー
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| securityConfig (field private org.springframework.security.core.userdetails.UserDetailsService com.example.demo.config.SecurityConfig.userDetailsService)
↑ ↓
| userDetailServiceImpl (field private com.example.demo.domain.user.service.UserService com.example.demo.domain.user.service.impl.UserDetailServiceImpl.service)
↑ ↓
| userServiceImpl (field private org.springframework.security.crypto.password.PasswordEncoder com.example.demo.domain.user.service.impl.UserServiceImpl.encoder)
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
上記のエラーの原因は調べたところ、
userServiceImpl クラス、
userDetailServiceImpl クラス、
securityConfig クラス
の3つのクラスのフィールドがそれぞれのクラスのフィールドを参照しており、中でサイクルを起こしているというものだということが分かりましたが、解決方法がわかりません。
該当するソースコード
SecurityConfigクラス
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/** セキュリティの対象外を設定 */
@Override
public void configure(WebSecurity web) throws Exception {
// セキュリティを適用しない
web
.ignoring()
.antMatchers("/webjars/**")
.antMatchers("/css/**")
.antMatchers("/js/**")
.antMatchers("/h2-console/**");
}
/** セキュリティの各種設定 */
@Override
protected void configure(HttpSecurity http) throws Exception {
// ログイン不要ページの設定
http
.authorizeRequests()
.antMatchers("/login").permitAll() //直リンクOK
.antMatchers("/user/signup").permitAll() //直リンクOK
.antMatchers("/admin").hasAuthority("ROLE_ADMIN") // 権限制御
.anyRequest().authenticated(); // それ以外は直リンクNG
// ログイン処理
http
.formLogin()
.loginProcessingUrl("/login") // ログイン処理のパス
.loginPage("/login") // ログインページの指定
.failureUrl("/login?error") // ログイン失敗時の遷移先
.usernameParameter("userId") // ログインページのユーザーID
.passwordParameter("password") // ログインページのパスワード
.defaultSuccessUrl("/user/list", true); // 成功後の遷移先
// ログアウト処理
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout");
// CSRF対策を無効に設定(一時的)
//http.csrf().disable();
}
/** 認証の設定 */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = passwordEncoder();
// インメモリ認証
/*
auth
.inMemoryAuthentication()
.withUser("user") // userを追加
.password(encoder.encode("user"))
.roles("GENERAL")
.and()
.withUser("admin") // adminを追加
.password(encoder.encode("admin"))
.roles("ADMIN");
*/
// ユーザーデータ認証
auth
.userDetailsService(userDetailsService)
.passwordEncoder(encoder);
}
}
UserServiceImplクラス
package com.example.demo.domain.user.service.impl;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.example.demo.domain.user.model.MUser;
import com.example.demo.domain.user.service.UserService;
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private UserService service;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// ユーザー情報取得
MUser loginUser = service.getLoginUser(username);
// ユーザーが存在しない場合
if(loginUser == null) {
throw new UsernameNotFoundException("user not found");
}
// 権限List作成
GrantedAuthority authority = new SimpleGrantedAuthority(loginUser.getRole());
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(authority);
// UserDetails生成
UserDetails userDetails = (UserDetails) new User(loginUser.getUserId(), loginUser.getPassword(), authorities);
return userDetails;
}
}
UserServiceクラス
package com.example.demo.domain.user.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.domain.user.model.MUser;
import com.example.demo.domain.user.service.UserService;
import com.example.demo.repository.UserMapper;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper mapper;
@Autowired
private PasswordEncoder encoder;
/** ユーザー登録 */
@Override
public void signup(MUser user) {
user.setDepartmentId(1); // 部署
user.setRole("ROLE_GENERAL"); // ロール
// パスワード暗号化
String rawPassword = user.getPassword();
user.setPassword(encoder.encode(rawPassword));
mapper.insertOne(user);
}
/** ユーザー取得 */
@Override
public List<MUser> getUsers(MUser user) {
return mapper.findMany(user);
}
/** ユーザー取得(1件) */
@Override
public MUser getUserOne(String userId) {
return mapper.findOne(userId);
}
/** ユーザー更新(1件) */
@Transactional
@Override
public void updateUserOne(String userId,
String password,
String userName) {
// パスワード暗号化
String encryptPassword = encoder.encode(password);
mapper.updateOne(userId, encryptPassword, userName);
// 例外を発生させる
// int i = 1 / 0;
}
/** ユーザー削除(1件) */
@Override
public void deleteUserOne(String userId) {
int count = mapper.deleteOne(userId);
}
/** ログインユーザー情報取得 */
@Override
public MUser getLoginUser(String userId) {
return mapper.findLoginUser(userId);
}
}
自分で試したこと
解決方法を調べたところ、どうやら@Lazyというアノテーションを使用すれば解決できるとのことなのですが、
あまり詳しい利用方法が載っておらず、困っている状況です。
@Lazyで解決で切るのであれば、よろしければ具体的な使用方法や解説も含めてご教示いただけますと幸いです。
また、@Lazy以外に解決方法があるのであれば、そちらでも結構ですので、ご教示の程よろしくお願い致します。
0 likes