通过@Autowired访问时的NPE

NPE while accessing through @Autowired

我的 SpringBoot 项目中有以下代码,它在提到的特定行中抛出 NPE。

Exception in thread "main" java.lang.NullPointerException
  at com.temp.controller.Controller.triggerJob(Controller.java:15)
  at com.temp.Application.main(Application.java:19)

Application.java

@Configuration
@SpringBootApplication
@ImportResource({"classpath:applicationContext.xml"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        
        Controller controller = new Controller();
        controller.triggerJob();
    }
}

Controller.java

@Controller
public class Controller {
    @Autowired
    private Service Service;
    
    public void triggerJob() {
        Service.selectRecords();
    }
}

Service.selectRecords(); is where the NPE is being thrown

Service.java

public interface Service {
    List<VO> selectRecords();
}

ServiceImpl.java

@Service
public class ServiceImpl implements Service {
    @Autowired
    private Dao dao;

    @Override
    public List<VO> selectRecords() {
        return dao.selectRecords();
    }
}

applicationContext.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:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util" 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.2.xsd
                            http://www.springframework.org/schema/jdbc
                            http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
                            http://www.springframework.org/schema/util 
                            http://www.springframework.org/schema/util/spring-util-3.2.xsd
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <util:properties id="configProperties"
        location="file:${app.config.home}/config/config-${spring.profiles.active}.properties" />

    <context:property-placeholder
        location="file:${app.config.home}/config/config-${spring.profiles.active}.properties" />

    <bean id="crypt" class="com.temp.util.MyUtil">
        <property name="userId" value="${username}"></property>
        <property name="password" value="${password}"></property>
        <property name="key" value="123456789012345678901234"></property>
    </bean>

    <bean id="datasource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${connection}" />
        <property name="username" value="#{crypt.userId}" />
        <property name="password" value="#{crypt.password}" />
    </bean>

    <bean id="namedJdbcTemplate"
        class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg ref="datasource" />
    </bean>
</beans>

我有一个类似的项目,比较了两者的配置,除了两点外它们是相同的。

  1. 本项目的 spring 启动版本是 2.4.2,另一个项目是 1.5.3。
  2. Java 这个项目的版本是 11,另一个项目的版本是 1.8。

不确定我哪里出错了。

我错过了什么吗?请帮忙。

您正在创建新实例 Controller controller = new Controller(); 手动并且此实例不在 Spring 上下文中。因此,服务的注入(自动装配)实例为空。 @Autowired 仅当实例存在于 Spring 上下文中时才有效。

保持 Controller 可测试的最佳方法是通过构造函数注入:

@Configuration
@SpringBootApplication
@ImportResource({"classpath:applicationContext.xml"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        
        Controller controller = new Controller(new ServiceImpl(new DaoImpl()));
        controller.triggerJob();
    }
}

通过构造函数注入实例:

    @Controller
    public class Controller {
        
        private final Service Service;
    
        public Controller(final Service Service) {
           this.service = service;
        }
        
        public void triggerJob() {
            Service.selectRecords();
        }
    }

并通过构造函数注入 Dao 依赖:

@Service
public class ServiceImpl implements Service {
    
    private final Dao dao;

    public ServiceImpl(final Dao dao) {
       this.dao = dao;
    }

    @Override
    public List<VO> selectRecords() {
        return dao.selectRecords();
    }
}

对于Spring 4.3 以上的版本,@Autowired 可以从构造函数的顶部省略,Spring 会自动扫描注入并通过构造函数注入依赖项。 对于 4.3 以下的 Spring 版本,在构造函数之上添加 @Autowired 即:

@Controller
public class Controller {

    private final Service Service;
    
    @Autowired
    public Controller(final Service Service) {
       this.service = service;
    }

    public void triggerJob() {
        Service.selectRecords();
    }
}