Reputation: 2527
I'm getting a NullPointerException for userDao
in this method. userDao
is indeed null, but why and how do I fix it?
NOTE! Question has been updated based on suggestions from several people.
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Override
public UserDao getUserDao() {
return userDao; // userDao is null
}
@Autowired
@Override
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public User add(User user) {
return getUserDao().insert(user);
}
@Override
public User get(String username) {
return getUserDao().select(username);
}
@Override
public boolean userExists(String username) {
return getUserDao().userExists(username); // throws NullPointerException (userDao is null -- confirmed with debugger)
}
springmvc-config.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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="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">
<bean id="tweetService"
class="com.springapp.mvc.tweet.TweetServiceImpl">
<property name="tweetDao" ref="tweetDao"/>
</bean>
<bean id="tweetDao"
class="com.springapp.mvc.tweet.TweetDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="userService"
class="com.springapp.mvc.user.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao"
class="com.springapp.mvc.user.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="followService"
class="com.springapp.mvc.follow.FollowServiceImpl">
<property name="followDao" ref="followDao"/>
</bean>
<bean id="followDao"
class="com.springapp.mvc.follow.FollowDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>User.hbm.xml</value>
<value>Tweet.hbm.xml</value>
<value>Follow.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="dialect">org.hibernate.dialect.H2Dialect</prop>
<prop key="current_session_context_class">thread</prop>
<prop key="hbm2ddl.auto">update</prop>
<prop key="connection.pool_size">1</prop>
</props>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver"/>
<property name="url" value="jdbc:h2:tcp://localhost/~/twitter"/>
<property name="username" value=""/>
<property name="password" value=""/>
</bean>
<bean id="dbUtil"
class="com.springapp.mvc.util.DbUtil"
init-method="initialize">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- register all beans except controllers -->
<context:component-scan base-package="com.springapp.mvc">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
</beans>
web.xml
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Spring MVC Application</display-name>
<!-- intercepts requests -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!-- protected URL path -->
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- location of Config -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContext-security.xml
classpath:/springmvc-config.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- dispatches requests to controllers -->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- maps requests to path to mvc-dispatcher -->
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
mvc-dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.springapp.mvc">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
<mvc:annotation-driven/>
<mvc:resources mapping="/css/**" location="/css/"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:resources mapping="/js/**" location="/js/**"/>
<mvc:resources mapping="/css/**" location="/css/**"/>
<mvc:resources mapping="/fonts/**" location="/fonts/**"/>
</beans>
applicationContext-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- security context -->
<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-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http auto-config="true">
<intercept-url pattern="/home/**" access="IS_AUTHENTICATED_FULLY"/>
<intercept-url pattern="/users/**" access="IS_AUTHENTICATED_FULLY"/>
<intercept-url pattern="/tweet/**" access="IS_AUTHENTICATED_FULLY"/>
<form-login
login-page="/login"
default-target-url="/login?success"
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password"
/>
<logout
logout-url="/logout"
logout-success-url="/"
/>
</http>
<authentication-manager>
<authentication-provider user-service-ref="userAccountDetailsService">
</authentication-provider>
</authentication-manager>
<beans:bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>
</beans:beans>
UserController.java
@Controller
public class UserController {
private static final Log logger = LogFactory.getLog(UserController.class);
@Autowired
private UserService userService;
@Autowired
private TweetService tweetService;
@Autowired
private FollowService followService;
@RequestMapping(value = {"/"})
public String landingPage() {
logger.info("landingPage called");
return "LandingPage";
}
@RequestMapping("/register")
public ModelAndView registerUser() {
logger.info("registerUser called");
ModelAndView model = new ModelAndView();
User user = new User();
model.addObject(user);
model.setViewName("RegistrationForm");
return model;
}
@RequestMapping(value = "/user_save", method = RequestMethod.POST)
public String saveUser(@ModelAttribute User user, BindingResult bindingResult) {
logger.info("saveUser called");
// validate form input
UserValidator userValidator = new UserValidator();
userValidator.validate(user, bindingResult); // NullPointerException occurs on this line, see validator below
// if errors are found, return to registration form and display errors
if (bindingResult.hasErrors()) {
if (bindingResult.hasErrors()) {
FieldError fieldError = bindingResult.getFieldError();
logger.info("Code: " + fieldError.getCode() + ", field: " + fieldError.getField());
return "RegistrationForm";
}
}
// saveTweet user to database
userService.add(user);
return "LoginForm";
}
...
Validator method:
...
public void validate(Object target, Errors errors) {
User user = (User) target;
// confirm that required fields are filled in
ValidationUtils.rejectIfEmpty(errors, "name", "name.required", "Error msg...");
ValidationUtils.rejectIfEmpty(errors, "username", "username.required", "Error msg...");
ValidationUtils.rejectIfEmpty(errors, "password", "password.required", "Error msg...");
// if the user has filled in a username, check if if already exists
String username = user.getUsername();
UserService userService = new UserServiceImpl();
if (username != null && userService.userExists(username)) { // this calls the userExists() method that causes a NullPointerException
errors.rejectValue("username", "username.exists", "Error msg...");
}
}
...
Upvotes: 0
Views: 8671
Reputation: 63991
You have enabled component-scan in mvc-dispatcher-servlet.xml
. That causes your UserServiceImpl
bean to be registered in two different contexts. The root application context (where it's registered because of the explicit XML configuration), and the web application context (where it's registed via component scanning).
(Check out this to see more on the difference between the two contexts)
The usual practice is to register all beans besides controllers in the root application context (in your case via springmvc-config.xml
). The controllers would then be registered in the web application context (in your case via mvc-dispatcher-servlet.xml
).
So in your case you change component scanning in springmvc-config.xml
to
<context:component-scan base-package="com.springapp.mvc">
<context:exclude-filter expression="org.springframework.stereotype.Controller"
type="annotation"/>
</context:component-scan>
and in mvc-dispatcher-servlet.xml
<context:component-scan base-package="com.springapp.mvc">
<context:include-filter expression="org.springframework.stereotype.Controller"
type="annotation"/>
</context:component-scan>.
The former component scan forces the Spring to pick up all classes (under com.springapp.mvc
) annotated with any stereotype annotation except @Controller
, while the latter one only includes @Controller
annotated classes.
Also you have the stereotype @Service
on UserServiceImpl
. Remove it since you are explicitly configuring the bean in XML and the class should not be picked up by component scanning.
On a separate note, the configuration
<mvc:annotation-driven/>
<mvc:resources mapping="/css/**" location="/css/"/>
belongs in mvc-dispatcher-servlet.xml
, not in springmvc-config.xml
.
UPDATE
You are obtaining the UserServiceImpl
using
UserService userService = new UserServiceImpl();
That mean that you are not using Spring to obtain it. You need to remove that and use the reference obtained from
@Autowired
private UserService userService
in the UserValidator
. That of course would mean that the UserValidator
has to be a Spring bean too.
So you have
@Component
public class UserValidator {
@Autowired
private UserService userService;
public void validate(Object target, Errors errors) {
User user = (User) target;
// confirm that required fields are filled in
ValidationUtils.rejectIfEmpty(errors, "name", "name.required", "Error msg...");
ValidationUtils.rejectIfEmpty(errors, "username", "username.required", "Error msg...");
ValidationUtils.rejectIfEmpty(errors, "password", "password.required", "Error msg...");
// if the user has filled in a username, check if if already exists
String username = user.getUsername();
if (username != null && userService.userExists(username)) { // this calls the userExists() method that causes a NullPointerException
errors.rejectValue("username", "username.exists", "Error msg...");
}
}
}
And you would need to add
@Autowired
private UserValidator userValidator
in the UserController
(while also removing UserValidator userValidator = new UserValidator()
)
Upvotes: 3
Reputation: 8975
Here in this case your object doesn't get injected and hence it is null.
You can use @Autowired annotation and you can enabled it by using component-scan of context namespace
<context:component-scan base-package="XXX"/>
Or define setter methods as I can see that you have given property in bean defination so you can give setter method for DAO which need to be injected.
xml file:
<bean id="userService"
class="com.springapp.mvc.user.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
UserServiceImpl
public void setUserDAO(UserDAO userDAO){
this.userDAO=userDAO;
}
Upvotes: -1