You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Service自动装配触发空指针异常(NPE)求助

问题:Spring Boot Service自动装配引发空指针异常(NPE)

我在Spring Boot项目中遇到了Service自动装配引发空指针异常(NPE)的问题,相关代码和项目结构如下:

主类

@SpringBootApplication
public class MicroserviceClientApplication implements ApplicationRunner {
    private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceClientApplication.class);

    private List<Client> clients = new ArrayList<Client>();

    @Autowired
    private Environment environment;
    @Autowired
    ApplicationContext applicationContext;

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder()
                .web(WebApplicationType.NONE)
                .sources(MicroserviceClientApplication.class)
                .headless(true)
                .build()
                .run(args);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        populateClients();
        LOGGER.info("Starting clients...");
        for (Client client : clients) {
            client.start();
        }
        runTest();
    }
}

Service类

package au.com.consunet.microserviceclient.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class WorldEnvironmentService {
    private static final Logger LOGGER = LoggerFactory.getLogger(WorldEnvironmentService.class);
    private static final double northWestCornerX = 138.4730987548828;
    private static final double northWestCornerY = -34.72359848022461;
    private static final double southEastCornerX = 138.70030212402344;
    private static final double southEastCornerY = -35.04520034790039;
    private static final int rowCount = 3574;
    private static final int colCount = 2071;
    private static final double colWidth = 1.0970708311956784E-4;
    private static final double rowHeight = 8.998373466026336E-5;

    public int normaliseLatitude(double latitudeDeg) {
        double diff = northWestCornerY - latitudeDeg;
        int diffCell = (int) (diff / rowHeight);
        return Math.min(Math.max(diffCell, 0), rowCount - 1);
    }

    public int normaliseLongitude(double longitudeDeg) {
        double diff = longitudeDeg - northWestCornerX;
        int diffCell = (int) (diff / colWidth);
        return Math.min(Math.max(diffCell, 0), colCount - 1);
    }
}

调用Service的类(触发NPE的类)

package au.com.consunet.microserviceclient.client;

import au.com.consunet.microserviceclient.client.request.HeightRequest;
import au.com.consunet.microserviceclient.model.Messages;
import au.com.consunet.microserviceclient.service.WorldEnvironmentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;

import java.util.HashMap;
import java.util.Map;

@Component
public class BoundingBoxRedisHashClient extends RedisHashClient {
    private static final Logger LOGGER = LoggerFactory.getLogger(BoundingBoxRedisHashClient.class);

    @Autowired
    private WorldEnvironmentService worldEnvironmentService;

    private Map<String, Response> getHeightsInBoundingBox(Messages.Location location) {
        double startLat = worldEnvironmentService.normaliseLatitude(Double.parseDouble(location.getLatitude()));
        double startLong = worldEnvironmentService.normaliseLongitude(Double.parseDouble(location.getLongitude()));
        double endLat = worldEnvironmentService.normaliseLatitude(Double.parseDouble(location.getEndLatitude()));
        double endLong = worldEnvironmentService.normaliseLongitude(Double.parseDouble(location.getEndLongitude()));

        Pipeline pl = jedis.pipelined();
        Map<String, Response> responses = new HashMap<>();
        ...
        pl.sync();
        return responses;
    }
    @Override
    protected void requestHeights() {
        LOGGER.info("Sending Redis Requests for RedisHash bounding box");
        for(int i=0;i<5;i++){
            getHeightsInBoundingBox(HeightRequest.getRandomLocation());
        }
    }

    @Override
    public void runTest() {
        requestHeights();
    }
}

项目结构

.
└── com.example.microserviceclient/
    ├── client/
    ├── model/
    ├── service/
    ├── world/
    └── MicroserviceClientApplication.java

调试确认:调用normalise系列方法时worldEnvironmentService确实为null。该服务已标注@Service,调用类也通过@Autowired注入,请问出现此问题的原因是什么?该如何解决?


原因分析及解决方案

核心原因

问题出在populateClients()方法中——你大概率是手动new了BoundingBoxRedisHashClient实例,而非从Spring容器中获取Bean。

Spring的自动装配仅对容器管理的Bean生效:如果通过new关键字创建对象,这个对象不属于Spring容器,Spring不会为其注入@Autowired标记的依赖,导致worldEnvironmentService为null。

解决方案

方案1:从Spring容器获取Client实例

在populateClients()方法中,通过applicationContext.getBean()方法获取容器中的BoundingBoxRedisHashClient实例,代替手动new:

private void populateClients() {
    // 从容器中获取Bean,而非new
    clients.add(applicationContext.getBean(BoundingBoxRedisHashClient.class));
    // 其他Client同理
}

方案2:让Spring自动注入Client集合

直接在主类中通过@Autowired注入所有实现Client接口的Bean,无需手动管理集合:

@SpringBootApplication
public class MicroserviceClientApplication implements ApplicationRunner {
    // ... 原有代码
    
    // 注入所有Client类型的Bean
    @Autowired
    private List<Client> clients;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        LOGGER.info("Starting clients...");
        for (Client client : clients) {
            client.start();
        }
        runTest();
    }
}

这种方式更符合Spring设计思想,Spring会自动收集所有实现Client接口的Bean并注入到集合中。

方案3:确保父类的Spring管理(若需)

如果RedisHashClient是普通类而非抽象类,需为其添加@Component或其他Spring注解,保证子类的依赖注入链条完整。

额外验证点

  1. 扫描范围验证:@SpringBootApplication默认扫描主类所在包及其子包,你的项目结构中主类在根包,Service和Client在子包下,扫描范围无问题。
  2. 服务实例验证:可在主类中通过applicationContext.containsBean(WorldEnvironmentService.class)检查服务是否被正确加载,返回true则说明服务实例正常。

内容的提问来源于stack exchange,提问作者Ben Whitely

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 03:15:06