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

Spring Boot使用@Autowired时Bean创建抛出NullPointerException的问题

错误根因

这根本不是@Autowired注入失效,本质是调用依赖的时机早于Spring的依赖注入时机。
Spring容器创建Bean的固定执行顺序是:

  1. 调用类的无参构造器,把对象实例先创建出来
  2. 给类上所有标注@Autowired的字段、方法注入对应的Bean实例
  3. 执行Bean的初始化回调方法,完成定制化初始化逻辑

你在ParkingServices的无参构造函数里,直接调用了parkingLot.init(10),这个执行节点停在整个流程的第1步,此时parkingLot字段还没经过第2步的注入,值是默认的null,自然会抛出空指针异常。这一点和错误堆栈里定位的空指针位置完全匹配——空指针正是出现在ParkingServices构造方法的对应代码行。

为什么手动new对象可以正常运行

如果不依赖Spring注入、直接手动new ParkingLotService()实例,整个创建流程的顺序完全由你自己控制:你可以先把parkingLot对象创建出来,再调用它的init方法,不存在空指针的问题。但这种方式完全脱离了Spring容器的管理,失去了依赖注入的核心优势,属于勉强能跑的临时写法。

正确修复方案

根据业务场景,有3种成熟的调整方式:

  • 调整初始化逻辑的执行时机:把原来写在构造器里的init调用,挪到依赖注入完成之后再执行
    直接用Spring提供的@PostConstruct注解标记初始化方法,该方法会在所有依赖注入完成后自动执行,不会出现空指针:
    @Service
    public class ParkingServices {
        @Autowired
        ParkingLotService parkingLot;
    
        // 所有@Autowired依赖注入完成后,自动执行该方法
        @PostConstruct
        public void initLot() {
            parkingLot.init(10);
        }
    
        public int parkCar(Vehicle vehicle) {
            return parkingLot.parkCar(vehicle);
        }
    }
    
  • 改用构造器注入替代字段注入
    这种方式会强制Spring在实例化ParkingServices的时候,先把所有依赖准备完成再传入构造方法,从根源上避免依赖未赋值就被调用的问题:
    @Service
    public class ParkingServices {
        private final ParkingLotService parkingLot;
    
        // Spring会自动将完整的ParkingLotService实例传入构造方法
        public ParkingServices(ParkingLotService parkingLot) {
            this.parkingLot = parkingLot;
            // 此时parkingLot已经是完整实例,可以直接调用方法
            parkingLot.init(10);
        }
    
        public int parkCar(Vehicle vehicle) {
            return parkingLot.parkCar(vehicle);
        }
    }
    
    这种也是Spring官方推荐的注入方式,比字段注入的耦合性更低、可测试性更强。
  • 解耦初始化逻辑,把车位初始化的职责放回ParkingLotService本身
    不需要让ParkingServices操心ParkingLotService的初始化过程,直接在ParkingLotService内部完成自己的初始化,两个类的职责更清晰:
    @Service
    public class ParkingLotService {
        // 车位大小配置后续可以挪到配置文件读取
        private static final int DEFAULT_CAPACITY = 10;
        private int size = DEFAULT_CAPACITY;
        private HashMap<Integer, Slot> parkingSlots = new HashMap<>();
        private Stack<Integer> availableSlots = new Stack<>();
    
        @PostConstruct
        public void init() {
            parkingSlots = new HashMap<>();
            for (int i = 0; i < size; i++) {
                parkingSlots.put(i, new ParkingSlot());
                availableSlots.push(i);
            }
        }
    
        public int parkCar(Vehicle vehicle) {
            int slotNo = availableSlots.pop();
            Slot slot = parkingSlots.get(slotNo);
            slot.parkVehicle(vehicle);
            parkingSlots.put(slotNo, slot);
            return slotNo;
        }
    
        public void removeCar(int parkingNumber) {
            Slot slot = parkingSlots.get(parkingNumber);
            // 补充原方法缺失的清空车位、回收车位号的逻辑
            slot.removeVehicle();
            availableSlots.push(parkingNumber);
        }
    }
    
    这种调整后,ParkingServices不需要写任何初始化代码,直接注入ParkingLotService就能正常使用。
附带的代码小问题提示

你的RestController里给Vehicle实体类加了@Autowired,这种写法是错误的:Vehicle是用来承载业务数据的实体类,不属于Spring管理的Bean,不需要交给容器注入,直接在接口方法里构建实例即可。


内容的提问来源于stack exchange,提问作者Alpit Anand

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 01:45:04