![](/img/trans.png)
[英]BeanNotOfRequiredTypeException: Bean named X is expected to be of type X but was actually of type 'com.sun.proxy.$Proxy
[英]Spring error “Bean named 'x' is expected to be of type 'y', but was actually of type [com.sun.proxy.$Proxy]”
我正在嘗試使用 Spring Security 在應用程序中實現基於 DAO 的身份驗證。
當我嘗試使用用戶登錄應用程序時,出現此錯誤:
failed to lazily initialize a collection of role: com.intellivest.app.dao.User.groups, could not initialize proxy - no Session
看着@jcmwright80 對這個問題的回答,我明白我最好將UserDetailsServiceImpl
erviceImpl class 注釋為@Transactional
。 這樣做之后,我在登錄時遇到錯誤:
Bean named 'userDetailsService' is expected to be of type 'com.intellivest.app.service.UserDetailsServiceImpl' but was actually of type 'com.sun.proxy.$Proxy238'"}}
這似乎是與在 UserDetailsServiceImpl 上創建的代理 object 相關的問題 - 我該如何優雅地解決這個問題?
代碼
安全配置的相關部分:
@Configuration
@ComponentScan("com.example.app.service")
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
}
UserDetailsServiceImpl.java
@Service
@Transactional
public class UserDetailsServiceImpl implements UserDetailsService{
public UserDetailsServiceImpl () {};
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.getUser(username);
if (user == null) {
throw new UsernameNotFoundException ("User not found.");
}
return new UserDetailsImpl(user);
}
}
用戶.java
@Entity
@Table(name="users",schema="sec")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="userGen")
@SequenceGenerator(name="userGen", sequenceName="user_id_seq", schema="sec")
private long id;
// Validation constraints on the fields ...
private String username;
private String password;
private boolean enabled;
@ManyToMany
@JoinTable(name="group_members", schema="sec", joinColumns= { @JoinColumn(name="user_id") }, inverseJoinColumns = { @JoinColumn(name="group_id") } )
private Set<Group> groups;
// Getters, Setters etc. ...
}
(在User
和Group
類的集合類型字段上使用@ManyToMany(fetch = FetchType.EAGER)
的替代解決方案有效,盡管它可能會影響性能。)
解決方案 1(最佳)
@Service
class 中使用的方法都應該在@Service
實現的接口中聲明。 然后可以在任何地方通過其接口類型引用該服務(例如在@Controller
類中)。 這樣 Spring 可以繼續使用 JDK 動態代理(優於 CGLIB)。
這一點尤其重要,因為實現了 Spring 提供的接口: org.springframework.security.core.userdetails.UserDetailsService
ervice 只聲明了一種方法,但也需要其他方法。
這與其說是一個簡單的編碼問題,不如說是一個架構問題。
一步步:
public interface CustomizedUserDetailsService extends UserDetailsService {
void add(User user);
}
實現服務中的方法:
@Service
public class UserDetailsServiceImpl implements CustomizedUserDetailsService{
@Override
@Transactional
public void add (User user) {
//...
}
}
通過@Configuration
中的接口類型引用 bean,但返回實現:
@Bean
@Primary
public CustomizedUserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
在使用實現時,通過對其實現接口的引用來注入 bean:
@Controller
public class UserController {
private CustomizedUserDetailsService userDetailsService;
@Autowired
public void setUsersService(CustomizedUserDetailsService customizedUserDetailsService) {
this.userDetailsService = customizedUserDetailsService;
}
}
更多關於代理的細節在這里。
解決方案 2
使用注解在服務 class 上啟用 CGLIB 代理。 這需要 spring-aop 和 aspectjweaver 依賴項(不使用 Spring Boot)。
@EnableAspectJAutoProxy(proxyTargetClass=true)
然后安全配置 class 可以像這樣生成 bean:
@Bean
public UserDetailsServiceImpl userDetailsService() {
return new UserDetailsServiceImpl();
}
(DAO 層不應該是@Transactional
,應該只應用於@Service
class 或后者 class 的特定方法。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.