2

I've written a Spring MVC application with a custom user details service.

@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);

At this point, userAccountDao is null, so it's throwing a null pointer exception on the above line, which would mean the autowiring isn't injecting this Dao within this service. Now the Dao itself has been autowired as such...

@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();
    }

Now this does work fine when I create users, get users from any other object, it's just this CustomUserDetailsService that's not being injected properly. It's in the same package com.securetest.app.service as other services that are able to use @Autowired fine.

I have 3 context.xml files - below is my 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>

This is my 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> 

and finally just to make sure I've not missed anything, my servlet-context.xml - as you can see, here I'm using context-component-scan and it should inject everything in 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>

I should mention, I'm pretty sure it's something like I've ordered my web.xml incorrectly or vice versa as almost exactly the same code works for another project but I can't see the difference between the two.

Why do I not get a autowire failure and just a nullPointerException?

EDIT : root-context.xml added below..

<?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>
2
  • What's in root-context.xml? Commented May 25, 2013 at 15:48
  • I haven't added anything to it, but it's shown above. Commented May 25, 2013 at 15:52

2 Answers 2

3

Your web.xml is creating two Spring application contexts. Let's call them Security (after security-context.xml) and Servlet (after servlet-context.xml). Security is created by the ContextLoaderListener listener, and Servlet is created by the DispatcherServlet servlet. Security is the parent of Servlet. That means that the beans in Security are only visible to other beans in Security, and the beans in Servlet can see both the beans in Security and in Servlet.

You're defining the CustomUserDetailsService (CUDS) bean in Security, and the UserAccountDao and UserDao beans in Servlet, so the CUDS bean can't see them. You need to add a component scan in Security for the DAO beans if you want them wired in to CUDS.

I'm not sure about the NPE.

Sign up to request clarification or add additional context in comments.

9 Comments

Err, right, because they're not being scanned. Does scanning them fix it?
I guess my answer was too terse. Editing it now.
So you're saying I should do another context:component-scan in my security-context.xml? This seems to create a beanCreationException
Move the component scan from Servlet to Security, the beans should be accessible to both contexts that way.
This stops all the autowiring working and the app 404's on startup with the following exception unfortunately pastebin.com/tGbuRJep
|
1

I think, your security context loads before your servlet-context, which holds the configuration for your DAOs. When security loads, DAOs haven't been scanned, hence nothing to inject! Use @Required to check.

I do the following for my project :

in 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>

in servlet-context :

<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>

I put the root-context.xml & security.xml in context-param and servlet-context.xml in the DispatcherServlet's init-param. This method also adds modularity. It keeps your WebApplicationContext related beans in one file which is then passed to your DispatcherServlet and your other ApplicationContext beans in a separate file.

7 Comments

This sounds exactly like it. Is the ordering defined by the web.xml or just the order of the items in the context.xml files?
in the web.xml.. context-params load first. For the time being, just xml inject the dao's and check instead of annotations.
@Autowired beans are required by default.
You have to set it on a setter. The application will throw an exception during start up.
Check the link.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.