spring @Autowired给出NullPointerException

时间:2018-02-05 11:44:13

标签: java spring spring-mvc spring-security

我通过Hibernate学习Spring并使用简单的登录进行演示项目。我坚持使用@Autowired给出NullPointerException。我没有得到它背后的正确理由,但我猜它是因为Spring无法在其上实例化bean。

仅当我使用 customAuthenticationProvider 登录时才会出现问题。如果我使用带有用户名和密码的默认<user-service>作为admin,代码运行正常。

这是我的代码,

的web.xml

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
     http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">

  <display-name>Archetype Created Web Application</display-name>

  <servlet>
      <servlet-name>dispatcher</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
      <servlet-name>dispatcher</servlet-name>
      <url-pattern>/</url-pattern>
  </servlet-mapping>

  <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
        /WEB-INF/spring-security.xml
      </param-value>
  </context-param>

  <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

<!-- Spring Security -->
  <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>

  <context-param>
      <param-name>log4jConfigLocation</param-name>
      <param-value>classpath:log4j.properties</param-value>
  </context-param>

  <listener>
      <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
  </listener>

</web-app>

调度-servlet.xml中

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

<mvc:annotation-driven />
<context:component-scan base-package="com.ssb" />
<context:annotation-config />

<bean
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url"
        value="jdbc:mysql://localhost:3306/springHibernateDemo" />
    <property name="username" value="root" />
    <property name="password" value="admin" />
</bean>

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="com.ssb.model" />
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
        </props>
    </property>
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />

<bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

</beans>

弹簧security.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://www.springframework.org/schema/security"
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 pattern="/resources/**" security="none" />

<http auto-config="true" use-expressions="true">
    <http-basic/>
    <intercept-url pattern="/login" access="permitAll"/>
    <intercept-url pattern="/**" access="isAuthenticated()" />
    <form-login login-page="/login" default-target-url="/home"
        authentication-failure-url="/login?error" login-processing-url="/j_spring_security_check"
        username-parameter="username" password-parameter="password" />
    <logout logout-success-url="/login?logout" logout-url="/j_spring_security_logout"/>
    <!-- enable csrf protection -->
    <csrf/>
</http>

<beans:bean id="customAuthenticationProvider"
    class="com.ssb.components.CustomAuthenticationProvider">
</beans:bean>

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="admin" password="admin" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
    <authentication-provider ref="customAuthenticationProvider">
    </authentication-provider>
</authentication-manager>

</beans:beans>

CustomAuthenticationProvider.java

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    public AuthService authService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        authService.authenticateUser(authentication);
        // it never gets to this line as authService is null
        return new UsernamePasswordAuthenticationToken(authentication.getName(), "");
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return true;
    }

}

HomeController.java

@Controller
public class HomeController {

  @Autowired
  HomeService homeService;

  @Autowired
  SessionFactory sessionFactory;

  @RequestMapping(value="/home", method=RequestMethod.GET)
  public String home(Model model){

      User user = new User();
      user.setName("test");
      user.setPassword("test");
      homeService.save(user);

      return "home";
  }

  @RequestMapping(value= "/login", method=RequestMethod.GET)
  public ModelAndView login(
          @RequestParam(value = "error", required = false) String error,
          @RequestParam(value = "logout", required = false) String logout) throws IOException {

          ModelAndView model = new ModelAndView();
          if (error != null) {
              model.addObject("error", "Invalid username and password!");
          }
          if (logout != null) {
            model.addObject("msg", "You've been logged out successfully.");
          }
          model.setViewName("login");
          return model;
      }

}

AuthServiceImpl.java

@Service
public class AuthServiceImpl implements AuthService{

  @Autowired
  HomeDao homeDao;

  @Transactional
  public void authenticateUser(Authentication authentication) {
    homeDao.authenticateUser(authentication);
  }

}

HomeDao.java

@Repository
public class HomeDao {

  @Autowired
  SessionFactory sessionFactory;

  public void save(User user){
      Session session = sessionFactory.getCurrentSession();
      Roles role = (Roles) session.get(Roles.class, 1);
      user.setRole_id(role);
      session.save(user);
  }

  public void authenticateUser(Authentication authentication) {
      Criteria criteria =  sessionFactory.getCurrentSession().createCriteria(User.class);
      criteria.add(Restrictions.eq("username", authentication.getPrincipal().toString()));
      criteria.add(Restrictions.eq("password", authentication.getCredentials().toString()));
      @SuppressWarnings("unchecked")
      List<User> result = criteria.list();
      System.out.println(result);

  }

}

错误

SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/springAct] threw exception
java.lang.NullPointerException
at com.ssb.components.CustomAuthenticationProvider.authenticate(CustomAuthenticationProvider.java:20)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:192)
at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:93)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:120)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:474)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:495)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:767)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1347)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)

2 个答案:

答案 0 :(得分:1)

您正在加载spring-security.xml作为spring root上下文,并且在此上下文中没有任何context:component-scan,因此在尝试使用@Autowired bean时接收NPE是绝对正常的在已在mvc上下文中加载的根上下文中,这是一个子上下文。

您可以在此答案中获得更多信息:https://stackoverflow.com/a/30640404/4190848

为避免此类错误,我会执行以下操作:

  1. dispatcher-servlet.xml context:component-scan映射定型中的@Controller地图中。
  2. 例如:

    <context:component-scan base-package="com.ssb.controller" />
    
    1. 当您仅使用spring-security.xml作为根上下文时,您应该映射使用@Service@Repository@Component映射的任何其他构造型。
    2. 例如:

      <context:component-scan base-package="com.ssb.repositories" />
      <context:component-scan base-package="com.ssb.services" />
      
      1. 同样,您应该将dispatcher-servlet.xml中定义的一些bean移动到spring-security.xml
      2. 就是这些:

        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
            <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            <property name="url"
                value="jdbc:mysql://localhost:3306/springHibernateDemo" />
            <property name="username" value="root" />
            <property name="password" value="admin" />
        </bean>
        
        <bean id="sessionFactory"
            class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="packagesToScan" value="com.ssb.model" />
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.hbm2ddl.auto">update</prop>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.show_sql">true</prop>
                </props>
            </property>
        </bean>
        
        <tx:annotation-driven transaction-manager="transactionManager" />
        
        <bean id="transactionManager"
            class="org.springframework.orm.hibernate4.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
        

        因此,生成的dispathcer-servlet.xmlspring-security.xml应该是这样的,只在context:component-scan base-packages中设置正确的包:

        dispatcher.servlet.xml:

        <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xmlns:mvc="http://www.springframework.org/schema/mvc"
            xsi:schemaLocation="http://www.springframework.org/schema/beans 
               http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
               http://www.springframework.org/schema/context 
               http://www.springframework.org/schema/context/spring-context-3.0.xsd
               http://www.springframework.org/schema/mvc 
               http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        
            <mvc:annotation-driven />
            <context:component-scan base-package="com.ssb.controller" />
            <context:annotation-config />
        
            <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                <property name="prefix" value="/WEB-INF/views/" />
                <property name="suffix" value=".jsp" />
            </bean>
        
        </beans>
        

        弹簧security.xml文件:

        <?xml version="1.0" encoding="UTF-8"?>
        <beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xmlns="http://www.springframework.org/schema/security"
          xmlns:tx="http://www.springframework.org/schema/tx" 
          xmlns:context="http://www.springframework.org/schema/context"
          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://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
        
            <context:component-scan base-package="com.ssb" />
        
            <beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
                <beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
                <beans:property name="url"
                value="jdbc:mysql://localhost:3306/springHibernateDemo" />
                <beans:property name="username" value="root" />
                <beans:property name="password" value="admin" />
            </beans:bean>
        
            <beans:bean id="sessionFactory"
            class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
                <beans:property name="dataSource" ref="dataSource" />
                <beans:property name="packagesToScan" value="com.ssb.model" />
                <beans:property name="hibernateProperties">
                    <beans:props>
                        <beans:prop key="hibernate.hbm2ddl.auto">update</beans:prop>
                        <beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect
                        </beans:prop>
                        <beans:prop key="hibernate.show_sql">true</beans:prop>
                    </beans:props>
                </beans:property>
             </beans:bean>
        
            <tx:annotation-driven transaction-manager="transactionManager" />
        
            <beans:bean id="transactionManager"
            class="org.springframework.orm.hibernate4.HibernateTransactionManager">
                <beans:property name="sessionFactory" ref="sessionFactory" />
            </beans:bean>
        
            <http pattern="/resources/**" security="none" />
        
            <http auto-config="true" use-expressions="true">
                <http-basic />
                <intercept-url pattern="/login" access="permitAll" />
                <intercept-url pattern="/**" access="isAuthenticated()" />
                <form-login login-page="/login" default-target-url="/home"
                authentication-failure-url="/login?error" login-processing-url="/j_spring_security_check"
                username-parameter="username" password-parameter="password" />
                <logout logout-success-url="/login?logout" logout-url="/j_spring_security_logout" />
                <!-- enable csrf protection -->
                <csrf />
            </http>
        
            <beans:bean id="customAuthenticationProvider"
                class="com.ssb.components.CustomAuthenticationProvider">
            </beans:bean>
        
            <authentication-manager>
                <authentication-provider>
                    <user-service>
                        <user name="admin" password="admin" authorities="ROLE_USER" />
                    </user-service>
                </authentication-provider>
                <authentication-provider ref="customAuthenticationProvider">
                </authentication-provider>
            </authentication-manager>
        
        </beans:beans>
        

答案 1 :(得分:0)

尝试在dispatcher-servlet.xml末尾放置context:component-scan base-package =“com.ssb”它可能有效,因为在ioc容器中放置bean时需要依赖,因此在创建AuthService bean时需要依赖未在IOC容器中创建的类。确保所有依赖项配置正确的顺序