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注解,保证子类的依赖注入链条完整。
额外验证点
- 扫描范围验证:
@SpringBootApplication默认扫描主类所在包及其子包,你的项目结构中主类在根包,Service和Client在子包下,扫描范围无问题。 - 服务实例验证:可在主类中通过
applicationContext.containsBean(WorldEnvironmentService.class)检查服务是否被正确加载,返回true则说明服务实例正常。
内容的提问来源于stack exchange,提问作者Ben Whitely
相关产品推荐
相关产品推荐

