【问题标题】:NullPointerException for DAODAO 的 NullPointerException
【发布时间】:2014-10-06 21:24:23
【问题描述】:

我在这个方法中得到了 userDao 的 NullPointerException。 userDao 确实为空,但为什么以及如何修复它?

注意!已根据几个人的建议更新了问题。

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";
}
...

验证器方法:

...
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...");
    }
}
...

【问题讨论】:

  • @Autowired 缺少 userDao 属性。
  • MVC 的东西通常是在 DispatcherServlet 下定义的?你的选择有什么理由吗?另外,你有mvc-dispatcher-servlet.xml 文件吗?
  • 您在mvc-dispatcher-servlet.xml 中启用了component-scan。您应该将其更改为仅扫描控制器所在的包。
  • 哦,那好吧:-)。您可以做的最简单的测试:从contextConfigLocation 中删除springmvc-config.xml。将springmvc-config.xml 下的所有内容移至mvc-dispatcher-servlet.xml。从生成的mvc-dispatcher-servlet.xml 中的所有component-scans 中,只保留引用控制器的那个。再试一次。
  • UserService userService = new UserServiceImpl(); 是禁忌:-)。

标签: spring hibernate dao sessionfactory


【解决方案1】:

您已在mvc-dispatcher-servlet.xml 中启用组件扫描。这会导致您的 UserServiceImpl bean 在两个不同的上下文中注册。根应用程序上下文(由于显式 XML 配置而注册的位置)和 Web 应用程序上下文(通过组件扫描注册的位置)。

(查看this 了解更多关于这两种上下文之间的差异)

通常的做法是在根应用程序上下文中注册除控制器之外的所有 bean(在您的情况下通过 springmvc-config.xml)。然后控制器将在 Web 应用程序上下文中注册(在您的情况下通过 mvc-dispatcher-servlet.xml)。

因此,在您的情况下,您将 springmvc-config.xml 中的组件扫描更改为

<context:component-scan base-package="com.springapp.mvc">
    <context:exclude-filter expression="org.springframework.stereotype.Controller"
        type="annotation"/>
</context:component-scan>

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

前一个组件扫描强制 Spring 拾取除@Controller 之外的所有带有任何构造型注释的类(在com.springapp.mvc 下),而后一个仅包括@Controller 注释的类。

你在UserServiceImpl 上也有@Service 的刻板印象。删除它,因为您在 XML 中显式配置 bean,并且组件扫描不应拾取该类。

另外,配置

<mvc:annotation-driven/>
<mvc:resources mapping="/css/**" location="/css/"/>

属于mvc-dispatcher-servlet.xml,不属于springmvc-config.xml

更新

您正在使用

获取UserServiceImpl

UserService userService = new UserServiceImpl();

这意味着您没有使用 Spring 来获取它。 您需要删除它并使用从

获得的参考
@Autowired
private UserService userService

UserValidator。这当然意味着 UserValidator 也必须是 Spring bean。

所以你有

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

}

你需要添加

@Autowired
private UserValidator userValidator

UserController 中(同时删除UserValidator userValidator = new UserValidator()

【讨论】:

  • 非常感谢您的彻底回复,可能改进了我的代码,并可能通过多次组件扫描解决了未来的一些问题。然而,这个特殊问题仍然存在。我将在主要问题中更新我的代码以反映更改。
  • @CaptainHindsight 好的,我会检查更新的问题
  • @CaptainHindsight 你应该明确提到你已经更新了这个问题以帮助未来的观众
  • @CaptainHindsight,setUserDao 的服务类中不需要 @Autowired。注入是在 .xml 中完成的。另外,你在哪里使用这个服务类?
  • 从控制器调用该类。我将添加代码。
【解决方案2】:

在这种情况下,您的对象没有被注入,因此它为空。

你可以使用@Autowired注解,你可以通过使用上下文命名空间的组件扫描来启用它

<context:component-scan  base-package="XXX"/>

或者定义setter方法,我可以看到你在bean定义中给出了属性 所以你可以为需要注入的DAO提供setter方法。

xml 文件:

<bean id="userService"
      class="com.springapp.mvc.user.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
</bean>

用户服务实现

public void setUserDAO(UserDAO userDAO){
    this.userDAO=userDAO;
}

【讨论】:

  • 是的,完成了,请调试并查看 sopln(getUserDAO()) 我猜它没有给出 NullPointer。检查 sopln 输出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-27
  • 2016-10-13
  • 1970-01-01
  • 1970-01-01
  • 2018-01-14
  • 1970-01-01
相关资源
最近更新 更多