Spring Security循环bean依赖

我正在研究Vaadin spring应用程序。 我唯一可以说的是,用户的身份validation/授权必须通过jdbcTemplate查询数据库来jdbcTemplate 。 如何解决这个问题? 我正在使用Spring Boot 1.4.2.RELEASE。

 Description: The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | jdbcAccountRepository defined in file [repositories\JdbcAccountRepository.class] ↑ ↓ | securityConfiguration.WebSecurityConfig (field services.JdbcUserDetailsServicessecurity.SecurityConfiguration$WebSecurityConfig.userDetailsService) ↑ ↓ | jdbcUserDetailsServices (field repositories.JdbcAccountRepository services.JdbcUserDetailsServices.repository) └─────┘ 

原始代码如下所示:

AccountRepository:

 public interface AccountRepository { void createAccount(Account user) throws UsernameAlreadyInUseException; Account findAccountByUsername(String username); } 

JdbcAccountRepository:

 @Repository public class JdbcAccountRepository implements AccountRepository { private final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); private final JdbcTemplate jdbcTemplate; private final PasswordEncoder passwordEncoder; @Autowired public JdbcAccountRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) { this.jdbcTemplate = jdbcTemplate; this.passwordEncoder = passwordEncoder; } @Transactional @Override public void createAccount(Account user) throws UsernameAlreadyInUseException { try { jdbcTemplate.update( "insert into Account (firstName, lastName, username, password, role) values (?, ?, ?, ?, ?)", user.getFirstName(), user.getLastName(), user.getUsername(), passwordEncoder.encode( user.getPassword()), user.getRole() ); } catch (DuplicateKeyException e) { throw new UsernameAlreadyInUseException(user.getUsername()); } } @Override public Account findAccountByUsername(String username) { return jdbcTemplate.queryForObject( "select username, password, firstName, lastName, role from Account where username = ?", (rs, rowNum) -> new Account( rs.getString("username"), rs.getString("password"), rs.getString("firstName"), rs.getString("lastName"), rs.getString("role")), username ); } } 

JdbcUserDetailsS​​ervices:

 @Service public class JdbcUserDetailsServices implements UserDetailsService { private final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); @Autowired JdbcAccountRepository repository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { try { Account account = repository.findAccountByUsername(username); User user = new User( account.getUsername(), account.getPassword(), AuthorityUtils.createAuthorityList( account.getRole() ) ); return user; } catch (DataAccessException e) { LOGGER.debug("Account not found", e); throw new UsernameNotFoundException("Username not found."); } } } 

SecurityConfiguration:

 @Configuration @ComponentScan public class SecurityConfiguration { @Autowired ApplicationContext context; @Autowired VaadinSecurity security; @Bean public PreAuthorizeSpringViewProviderAccessDelegate preAuthorizeSpringViewProviderAccessDelegate() { return new PreAuthorizeSpringViewProviderAccessDelegate(security, context); } @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) public static class GlobalMethodSecurity extends GlobalMethodSecurityConfiguration { @Bean @Override protected AccessDecisionManager accessDecisionManager() { return super.accessDecisionManager(); } } @Configuration @EnableWebSecurity public static class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired JdbcUserDetailsServices userDetailsService; @Autowired DataSource dataSource; @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } @Bean public TextEncryptor textEncryptor() { return Encryptors.noOpText(); } /* * (non-Javadoc) * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter * #configure(org.springframework.security.config.annotation.web.builders.WebSecurity) */ @Override public void configure(WebSecurity web) throws Exception { //Ignoring static resources web.ignoring().antMatchers("/VAADIN/**"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } @Bean(name="authenticationManager") @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /* * (non-Javadoc) * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter * #configure(org.springframework.security.config.annotation.web.builders.HttpSecurity) */ @Override protected void configure(HttpSecurity http) throws Exception { http .exceptionHandling() .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")) .and() .authorizeRequests() .antMatchers("/**").permitAll() .and() .csrf().disable(); } } } 

PS如果将Spring Boot版本降级为[1.1.5,1.2.0],则不会出现此问题(由于其他依赖性,我必须使用最新版本)

您可以使用基于 setter的dependency injection替换基于构造函数的 dependency injection来解决循环,请参阅Spring Framework Reference Documentation :

循环依赖

如果您主要使用构造函数注入,则可以创建无法解析的循环依赖关系场景。

例如:类A通过构造函数注入需要类B的实例,而类B通过构造函数注入需要类A的实例。 如果为A类和B类配置bean以便相互注入,则Spring IoC容器会在运行时检测此循环引用,并抛出BeanCurrentlyInCreationException

一种可能的解决方案是编辑由setter而不是构造函数配置的某些类的源代码。 或者,避免构造函数注入并仅使用setter注入。 换句话说,尽管不推荐使用,但您可以使用setter注入配置循环依赖关系。

与典型情况(没有循环依赖)不同,bean A和bean B之间的循环依赖强制其中一个bean在完全初始化之前被注入另一个bean(一个经典的鸡/蛋场景)。

我更喜欢@Lazy方法。 这样我就可以坚持一种模式。

请参阅http://www.baeldung.com/circular-dependencies-in-spring