Spring Autowired空指针异常

Posted

技术标签:

【中文标题】Spring Autowired空指针异常【英文标题】:Spring Autowired Null Pointer Exception 【发布时间】:2017-07-02 02:54:15 【问题描述】:

我在使用 Spring-MVC (4.2.5)、Spring-Security (4.0.3) 和 MongoDB 开发一个非常简单的 Web 应用程序时遇到了一些问题。

我正在尝试实现一个自定义身份验证提供程序,以便使用 DAO 模式通过数据库登录用户。虽然它似乎已经正确初始化了应用程序上下文,但我在 Autowired 变量(CustomerDao 和 CustomUserDetailsS​​ervice)上有空指针异常。

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    id="WebApp_ID" version="3.1">
    <display-name>AcmeSpring</display-name>
    <servlet>
        <servlet-name>SpringController</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringController</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring-security.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>
</web-app>

spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mongo="http://www.springframework.org/schema/data/mongo"
    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
        http://www.springframework.org/schema/data/mongo 
        http://www.springframework.org/schema/data/mongo/spring-mongo-1.5.xsd">

    <mvc:annotation-driven />
    <context:component-scan base-package="controller" />
    <context:component-scan base-package="model" />
    <context:component-scan base-package="dao" />
    <context:component-scan base-package="service" />

    <mvc:resources mapping="/resources/**" location="/resources/" />

    <bean id="templateResolver"
        class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
        <property name="prefix" value="/WEB-INF/html/" />
        <property name="suffix" value=".html" />
        <property name="templateMode" value="HTML5" />
    </bean>

    <bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
        <property name="templateResolver" ref="templateResolver" />
    </bean>

    <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
        <property name="templateEngine" ref="templateEngine" />
        <property name="order" value="1" />
    </bean>

    <mongo:db-factory id="mongoDbFactory" host="localhost"
        port="27017" dbname="AcmeSpring" />

    <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg ref="mongoDbFactory" />
    </bean>

    <bean id="customerDao" class="dao.CustomerDaoImpl">
    </bean>
</beans>

spring-security.xml

<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-4.0.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-4.0.xsd">

    <http pattern="/resources/**" security="none" />

    <http auto-config="true">
        <intercept-url pattern="/user/**" access="hasRole('USER')" />
        <form-login authentication-failure-url="/login" login-page="/login"
            login-processing-url="/login" default-target-url="/user" />
        <logout invalidate-session="true" success-handler-ref="logoutSuccessHandler" />
    </http>

    <authentication-manager>
        <authentication-provider ref="customAuthenticationProvider" />
    </authentication-manager>

    <beans:bean id="customUserDetailsService" class="service.CustomUserDetailsService">
    </beans:bean>

    <beans:bean id="customAuthenticationProvider" class="service.CustomAuthenticationProvider">
    </beans:bean>

    <beans:bean id="logoutSuccessHandler" class="service.CustomLogoutSuccessHandler">
    </beans:bean>
</beans:beans>

CustomAuthenticationProvider

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider 
    @Autowired
    CustomUserDetailsService userDetails;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException 
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        System.out.println(userDetails == null);

        Customer customer = userDetails.loadUserByUsername(username);

        if(customer == null) 
            throw new BadCredentialsException("Username errato");
        

        if(!password.equals(customer.getPassword())) 
            throw new BadCredentialsException("Password errata");
        

        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority(customer.getRole()));

        return new UsernamePasswordAuthenticationToken(customer, password, authorities);
    

    @Override
    public boolean supports(Class<?> clazz) 
        return clazz.equals(UsernamePasswordAuthenticationToken.class);
    

CustomUserDetailsS​​ervice

@Service
public class CustomUserDetailsService implements UserDetailsService 
    @Autowired
    private CustomerDao customerDao;

    @Override
    public Customer loadUserByUsername(String username) throws UsernameNotFoundException 
        return customerDao.findCustomerByUsername(username);
    

CustomerDaoImpl

@Repository
public class CustomerDaoImpl implements CustomerDao 
    private static final String COLLECTION = "Customer";
    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public void create(Customer customer) 
        this.mongoTemplate.insert(customer, COLLECTION);
    

    @Override
    public Customer findCustomerByUsername(String username) 
        Query query = new Query();
        query.addCriteria(Criteria.where("username").is(username));
        System.out.println(query.toString());
        return this.mongoTemplate.findOne(query, Customer.class);
    

堆栈跟踪

java.lang.NullPointerException
    service.CustomUserDetailsService.loadUserByUsername(CustomUserDetailsService.java:18)
    service.CustomAuthenticationProvider.authenticate(CustomAuthenticationProvider.java:32)
    org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:192)
    org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:93)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:120)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)

我哪里错了? 提前谢谢你。

编辑:我没有手动初始化自动装配变量,只是粘贴不好,我已经编辑了我的代码。

EDIT2:按照@iMysak 的建议,我从 spring-mvc.xml 中删除了 customerDao bean 声明,并从 spring-security.xml 中删除了 3 个服务 bean 声明

新的堆栈跟踪

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#1' while setting bean property 'sourceList' with key [1]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#1': Cannot create inner bean '(inner bean)#5adbfd3' of type [org.springframework.security.web.authentication.logout.LogoutFilter] while setting constructor argument with key [4]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#5adbfd3': Cannot resolve reference to bean 'logoutSuccessHandler' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'logoutSuccessHandler' is defined
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:382)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:157)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1481)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1226)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:753)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:444)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:326)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4853)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5314)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#1': Cannot create inner bean '(inner bean)#5adbfd3' of type [org.springframework.security.web.authentication.logout.LogoutFilter] while setting constructor argument with key [4]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#5adbfd3': Cannot resolve reference to bean 'logoutSuccessHandler' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'logoutSuccessHandler' is defined
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:313)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:129)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:382)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:157)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:634)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:140)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1143)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
    ... 26 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#5adbfd3': Cannot resolve reference to bean 'logoutSuccessHandler' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'logoutSuccessHandler' is defined
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:634)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:140)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1143)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:299)
    ... 40 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'logoutSuccessHandler' is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:698)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1175)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
    ... 48 more

我还尝试按照here 的建议添加 aop、tx 和表达式依赖项,但我得到了上面的堆栈跟踪

【问题讨论】:

尝试在 spring-security.xml 中添加这一行: 是的,CustomerDao(接口)和CustomerDaoImpl都在dao包中 【参考方案1】:

使用多个context:component-scan 标签可能会出现问题。请尝试下一步:

<context:component-scan base-package="controller model dao service" />

而不是单独声明它们。

【讨论】:

即使按照您的建议更改组件扫描也会出现同样的错误 @andy 你能把这些遗漏的 bean 声明从 spring-security.xml 中移出并放入 spring-mvc.xml 吗? customUserDetailsS​​ervice 和其他人。只是为了确保 我试过了,但我得到一个“创建名为 'org.springframework.security.filterChains' 的 bean 时出错” 如果在 context:component-scan 下使用定义 daoservice 你甚至不应该声明它们在 xml 中,因此您甚至可以从 xml 中删除它们。您使用 filterChains 到底遇到了什么错误? 也请关注***.com/questions/19099289/…【参考方案2】:

你犯了经典的 Spring bean 错误。你注释了 bean:

@Autowired
CustomUserDetailsService userDetails;

你叫新人:

userDetails = new CustomUserDetailsService();

它要么在 Spring 控制之下,要么在你的控制之下。你调用 new 的那一刻,它就脱离了 Spring 的控制。

如果你想让 Spring 自动连接依赖,你不能调用 new 来初始化引用。让 Spring 来做吧。

【讨论】:

我的代码中并没有真正的 userDetails = new CustomUserDetailsS​​ervice(),抱歉粘贴不好:我试图删除所有 Autowired 注释并手动初始化 dao 和服务,回去我忘记删除该行。我知道如果这些是自动装配的,我不必初始化变量。我已经编辑了我的问题【参考方案3】:

你可以尝试摆脱

<beans:bean id="customAuthenticationProvider" class="service.CustomAuthenticationProvider">
</beans:bean>

<beans:bean id="customUserDetailsService" class="service.CustomUserDetailsService">
    </beans:bean>

我认为 XML 声明可能与注解声明和组件扫描是多余的

【讨论】:

删除这些行我得到一个“创建名称为 'org.springframework.security.filterChains' 的 bean 时出错”等。

以上是关于Spring Autowired空指针异常的主要内容,如果未能解决你的问题,请参考以下文章

使用服务的Spring安全性不会自动赋予依赖关系并提供空指针异常

使用@Autowired注解无法注入(使用service报空指针异常)的问题解决,亲测可用!

SpringBoot整合WebSocket时,自动注入Service层报错空指针异常的解决方案

导入(上传)Excel文件时报错空指针异常?

SpringBug记录 -- java.lang.NullPointerException在Spring单元测试中遇到的空指针异常及依赖注入异常总结

Spring自动装配空指针异常[重复]