2

我编写了一个带有自定义用户详细信息服务的 Spring MVC 应用程序。

@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService{

    @Autowired
    private UserAccountDao userAccountDao;

    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {

        UserDetails user = null;
        try {

            UserAccount dbUser = (UserAccount) userAccountDao.getByUsername(username);

此时,userAccountDao 为空,所以它在上面的行中抛出了一个空指针异常,这意味着自动装配没有在这个服务中注入这个 Dao。现在道本身已经自动装配成这样......

@Repository("userAccountDao")
public class UserAccountDaoImpl extends UserDaoImpl implements UserAccountDao {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public void addUserAccount(final UserAccount userAccount) {
        userAccount.setPassword(passwordEncoder.encodePassword(userAccount.getPassword(), "salt"));
        sessionFactory.getCurrentSession().save(userAccount);
    }
}



@Repository("userDao")
public class UserDaoImpl implements UserDao {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public User getByUsername(final String username) {
        return (User) sessionFactory.getCurrentSession()
                .createQuery("from User where username = :username")
                .setParameter("username", username).uniqueResult();
    }

现在,当我创建用户、从任何其他对象获取用户时,这确实可以正常工作,只是这个 CustomUserDetailsS​​ervice 没有被正确注入。com.securetest.app.service它与能够使用 @Autowired 的其他服务在同一个包中。

我有 3 个 context.xml 文件 - 下面是我的 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/root-context.xml
            /WEB-INF/spring/appServlet/security-context.xml</param-value>
    </context-param>
    <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml
        /WEB-INF/spring/appServlet/persistence-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

这是我的 security-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/security 
                    http://www.springframework.org/schema/security/spring-security-3.1.xsd
                    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <http use-expressions="true">
        <intercept-url pattern="/exam" access="isAuthenticated()" />
        <intercept-url pattern="/" access="permitAll" />
        <intercept-url pattern="/**" access="denyAll" />
        <form-login />
        <logout invalidate-session="true" logout-success-url="/"
            logout-url="/logout" />
    </http>
    <beans:bean id="CustomUserDetailsService"
        class="com.securetest.app.service.CustomUserDetailsService" />

    <beans:bean
        class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"
        id="passwordEncoder" />

    <authentication-manager>
        <authentication-provider user-service-ref='CustomUserDetailsService'>
            <password-encoder ref="passwordEncoder" />
        </authentication-provider>
    </authentication-manager>
</beans:beans> 

最后只是为了确保我没有遗漏任何东西,我的 servlet-context.xml - 如您所见,这里我使用的是 context-component-scan,它应该将所有内容注入com.securetest.app

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- DispatcherServlet Context: defines this servlet's request-processing 
        infrastructure -->

    <!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving 
        up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <context:component-scan base-package="com.securetest.app." />
    <!-- Resolves views selected for rendering by @Controllers to .jsp resources 
        in the /WEB-INF/views directory -->
    <beans:bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>
    <beans:bean
        class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <beans:property name="mediaTypes">
            <beans:map>
                <beans:entry key="html" value="text/html" />
                <beans:entry key="json" value="application/json" />
            </beans:map>
        </beans:property>
        <beans:property name="defaultViews">
            <beans:list>
                <beans:bean
                    class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
                    <beans:property name="prefixJson" value="true" />
                </beans:bean>
            </beans:list>
        </beans:property>
    </beans:bean>

</beans:beans>

我应该提一下,我很确定这就像我错误地订购了我的 web.xml,反之亦然,因为几乎完全相同的代码适用于另一个项目,但我看不出两者之间的区别。

为什么我没有遇到自动装配故障而只是出现 nullPointerException?

编辑:root-context.xml 添加在下面..

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


</beans>
4

2 回答 2

3

您的 web.xml 正在创建两个 Spring 应用程序上下文。我们称它们为 Security(在 security-context.xml 之后)和 Servlet(在 servlet-context.xml 之后)。安全性由 ContextLoaderListener 监听器创建,而 Servlet 由 DispatcherServlet servlet 创建。安全性是 Servlet 的父级。这意味着Security中的bean只对Security中的其他bean可见,而Servlet中的bean既可以看到Security中的bean,也可以看到Servlet中的bean。

您在 Security 中定义 CustomUserDetailsS​​ervice (CUDS) bean,在 Servlet 中定义 UserAccountDao 和 UserDao bean,因此 CUDS bean 看不到它们。如果希望将 DAO bean 连接到 CUDS,则需要在安全性中添加组件扫描。

我不确定NPE。

于 2013-05-25T15:56:46.270 回答
1

我认为,您的安全上下文在您的 之前加载servlet-context,它包含您的 DAO 的配置。当安全加载时,DAO 没有被扫描,因此没有什么可以注入的!用来@Required检查。

我为我的项目执行以下操作:

root-context.xml

<context:component-scan use-default-filters="true"
        base-package="com.trelta.accountmanagement, com.trelta.commons">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

在 servlet 上下文中:

<context:component-scan use-default-filters="false"
        base-package="com.trelta.accountmanagement, com.trelta.commons">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

我将root-context.xml& security.xml 放入和context-param' s中。这种方法还增加了模块化。它将您的相关 bean 保存在一个文件中,然后在一个单独的文件中传递给您和您的其他bean。servlet-context.xmlDispatcherServletinit-paramWebApplicationContextDispatcherServletApplicationContext

于 2013-05-25T16:00:40.423 回答