具有 spring 个注解驱动组件的依赖注入自动装配 Null
Dependency Injection Autowiring Null with spring annotaion-driven components
我创建了一个 spring-mvc 应用程序。配置如下所示:
调度员-servlet.xml
<beans ... >
<mvc:annotation-driven />
<context:annotation-config />
<aop:aspectj-autoproxy/>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<context:component-scan base-package="my.server.controller" />
<context:component-scan base-package="my.server.dao" />
<context:component-scan base-package="my.server.service" />
......
</beans>
applicationContext.xml
<beans ...>
...
<bean id="myUserDetailsService" class="my.server.service.MyUserDetailsService" autowire="byType"/>
</beans>
security.xml
<beans:beans ...>
<http pattern="/css/**" security="none"/>
<http pattern="/login.jsp*" security="none"/>
<http auto-config='true'>
<http-basic />
<logout />
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
<authentication-manager>
<authentication-provider user-service-ref="myUserDetailsService" >
<password-encoder hash="bcrypt" />
</authentication-provider>
</authentication-manager>
</beans:beans>
security.xml 和 applicationContext.xml 由 web.xml
中的以下行加载:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml, /WEB-INF/security.xml</param-value>
</context-param>
my.server.dao.UserDao.java
@Component
public interface UserDao {
JUser findByUserName(String username);
}
my.server.dao.UserDaoMemory.java
@Component("userDao")
public class UserDaoMemory implements UserDao {
private static List<JUser> myUsers = new ArrayList<>();
static {
myUsers.add(new JUser("ali", "123","ROLE_USER"));
}
@SuppressWarnings("unchecked")
@Override
public JUser findByUserName(String username) {
List<JUser> users = new ArrayList<>();
users = myUsers;
if (users.size() > 0) {
return users.get(0);
} else {
return null;
}
}
}
my.server.service.MyUserDetailsService.java
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(final String throws UsernameNotFoundException {
System.out.println("MyUserDetailsService#loadUserByUsername, userDao:"+userDao);
JUser user = userDao.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRoles());
return buildUserForAuthentication(user, authorities);
}
// Converts my.server.entity.JUser user to
// org.springframework.security.core.userdetails.User
private User buildUserForAuthentication(JUser user,
List<GrantedAuthority> authorities) {
return new User(user.getUsername(),
user.getPassword(), user.isEnabled(),
true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<JRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (JRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
public UserDao getUserDao() {
return userDao;
}
}
我的问题出在 MyUserDetailsServie
调用 loadUserByUsername(..)
的地方; userDao
尚未自动装配并且是 null
:
MyUserDetailsService#loadUserByUsername, userDao:null
一个可能的解决方案是更改 applicationContext.xml
例如(autowire="byName"
):
<bean id="myUserDetailsService" class="my.server.service.MyUserDetailsService" autowire="byName"/>
但它不起作用!!!!
您在上下文设置方面存在一些冲突。
applicationContext.xml(父上下文)通常是您定义所有共享组件(例如存储库、安全性、服务、处理程序...)的地方,您已正确完成这些组件。
您的 servlet 上下文(例如 dispatcher-servlet.xml)是一个子上下文,并且对父上下文中定义的所有内容都具有可见性。因此,您应该将组件的范围限制为 servlet 上下文的控制器,这意味着您应该只扫描您的控制器,但您正在扫描所有内容。
<context:component-scan base-package="my.server.controller" />
<context:component-scan base-package="my.server.dao" />
<context:component-scan base-package="my.server.service" />
应该是
<context:component-scan base-package="my.server.controller" />
您也可以将所有这些移动到您的 applicationContext
<mvc:annotation-driven />
<context:annotation-config />
<aop:aspectj-autoproxy/>
除非您专门为此 servlet 上下文使用它们。
无论如何,回到问题上来,因为子上下文正在扫描 service/dao 类,它会找到那些,但它们没有正确配置。
so where do I scan my.server.dao
暂时将它们放入 applicationContext.xml,随着应用程序复杂性的增加,将来将它们放入类似于 repositoryContext 的东西。
从技术上讲,您可以根据需要将所有内容转储到 servlet 上下文中,但是任何时候除了 servlet 上下文之外还定义 "root" 上下文,都会产生范围问题。子上下文中的所有内容(例如 servlet 上下文)都可以看到父上下文中的组件(例如 web.xml 上下文加载器加载的任何内容),但反之则不行。
我创建了一个 spring-mvc 应用程序。配置如下所示:
调度员-servlet.xml
<beans ... >
<mvc:annotation-driven />
<context:annotation-config />
<aop:aspectj-autoproxy/>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<context:component-scan base-package="my.server.controller" />
<context:component-scan base-package="my.server.dao" />
<context:component-scan base-package="my.server.service" />
......
</beans>
applicationContext.xml
<beans ...>
...
<bean id="myUserDetailsService" class="my.server.service.MyUserDetailsService" autowire="byType"/>
</beans>
security.xml
<beans:beans ...>
<http pattern="/css/**" security="none"/>
<http pattern="/login.jsp*" security="none"/>
<http auto-config='true'>
<http-basic />
<logout />
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
<authentication-manager>
<authentication-provider user-service-ref="myUserDetailsService" >
<password-encoder hash="bcrypt" />
</authentication-provider>
</authentication-manager>
</beans:beans>
security.xml 和 applicationContext.xml 由 web.xml
中的以下行加载:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml, /WEB-INF/security.xml</param-value>
</context-param>
my.server.dao.UserDao.java
@Component
public interface UserDao {
JUser findByUserName(String username);
}
my.server.dao.UserDaoMemory.java
@Component("userDao")
public class UserDaoMemory implements UserDao {
private static List<JUser> myUsers = new ArrayList<>();
static {
myUsers.add(new JUser("ali", "123","ROLE_USER"));
}
@SuppressWarnings("unchecked")
@Override
public JUser findByUserName(String username) {
List<JUser> users = new ArrayList<>();
users = myUsers;
if (users.size() > 0) {
return users.get(0);
} else {
return null;
}
}
}
my.server.service.MyUserDetailsService.java
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(final String throws UsernameNotFoundException {
System.out.println("MyUserDetailsService#loadUserByUsername, userDao:"+userDao);
JUser user = userDao.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRoles());
return buildUserForAuthentication(user, authorities);
}
// Converts my.server.entity.JUser user to
// org.springframework.security.core.userdetails.User
private User buildUserForAuthentication(JUser user,
List<GrantedAuthority> authorities) {
return new User(user.getUsername(),
user.getPassword(), user.isEnabled(),
true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<JRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (JRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
public UserDao getUserDao() {
return userDao;
}
}
我的问题出在 MyUserDetailsServie
调用 loadUserByUsername(..)
的地方; userDao
尚未自动装配并且是 null
:
MyUserDetailsService#loadUserByUsername, userDao:null
一个可能的解决方案是更改 applicationContext.xml
例如(autowire="byName"
):
<bean id="myUserDetailsService" class="my.server.service.MyUserDetailsService" autowire="byName"/>
但它不起作用!!!!
您在上下文设置方面存在一些冲突。
applicationContext.xml(父上下文)通常是您定义所有共享组件(例如存储库、安全性、服务、处理程序...)的地方,您已正确完成这些组件。
您的 servlet 上下文(例如 dispatcher-servlet.xml)是一个子上下文,并且对父上下文中定义的所有内容都具有可见性。因此,您应该将组件的范围限制为 servlet 上下文的控制器,这意味着您应该只扫描您的控制器,但您正在扫描所有内容。
<context:component-scan base-package="my.server.controller" />
<context:component-scan base-package="my.server.dao" />
<context:component-scan base-package="my.server.service" />
应该是
<context:component-scan base-package="my.server.controller" />
您也可以将所有这些移动到您的 applicationContext
<mvc:annotation-driven />
<context:annotation-config />
<aop:aspectj-autoproxy/>
除非您专门为此 servlet 上下文使用它们。
无论如何,回到问题上来,因为子上下文正在扫描 service/dao 类,它会找到那些,但它们没有正确配置。
so where do I scan my.server.dao
暂时将它们放入 applicationContext.xml,随着应用程序复杂性的增加,将来将它们放入类似于 repositoryContext 的东西。
从技术上讲,您可以根据需要将所有内容转储到 servlet 上下文中,但是任何时候除了 servlet 上下文之外还定义 "root" 上下文,都会产生范围问题。子上下文中的所有内容(例如 servlet 上下文)都可以看到父上下文中的组件(例如 web.xml 上下文加载器加载的任何内容),但反之则不行。