具有 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.xmlapplicationContext.xmlweb.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 上下文加载器加载的任何内容),但反之则不行。