Spring Boot控制器@Autowired注入DAO为null空指针问题咨询
出错原因
Spring 字段注入(给类成员变量加@Autowired的注入形式)的执行时机,是在Bean实例创建完成、构造方法执行结束之后。你在MessageBoxController中定义counter成员变量时直接调用dao.getLatestID()做初始化,这段逻辑属于Controller实例构造阶段的执行逻辑,此时dao还未完成注入,值为null,因此触发空指针异常。
代码里还存在两个隐藏风险:
getLatestID()方法声明返回值为long,但JDBC查询指定的结果映射类型是Integer.class,如果ID值超过int取值范围会抛出类型转换异常- 当
messages表为空时,你写的查询SQL会返回null,直接将null拆箱为基本数字类型也会触发空指针
修复方式
提供两种常用的正确实现,任选其一即可:
方案1:构造器注入(Spring官方推荐)
构造器注入能保证依赖在对象实例化阶段就完成赋值,不会出现注入时机晚于初始化逻辑的问题:
@RestController public class MessageBoxController { private final MessageBoxDAO dao; private final AtomicLong counter; // Spring 4.3及以上版本,类只有一个构造器时无需额外加@Autowired注解 public MessageBoxController(MessageBoxDAO dao) { this.dao = dao; // 此时dao已完成注入,可正常调用方法 this.counter = new AtomicLong(dao.getLatestID()); } // 其余业务逻辑省略 }
同步修复DAO层的类型问题和空值处理:
@Component public class MessageBoxDAO { @Autowired JdbcTemplate jdbcTemplate; public long getLatestID() { // 结果类型改为Long.class,和方法返回值类型匹配 Long maxId = jdbcTemplate.queryForObject("SELECT id FROM messages ORDER BY id DESC LIMIT 1", Long.class); // 表为空时返回null,默认初始值设为0,可根据业务需求调整 return maxId == null ? 0L : maxId; } }
方案2:使用@PostConstruct初始化
如果习惯用字段注入,可以把计数器初始化逻辑放到@PostConstruct标注的方法中,该方法会在所有依赖注入完成后由Spring自动调用:
@RestController public class MessageBoxController { @Autowired MessageBoxDAO dao; private AtomicLong counter; @PostConstruct public void initCounter() { // 执行到该方法时,dao已经完成注入,不会触发空指针 this.counter = new AtomicLong(dao.getLatestID()); } // 其余业务逻辑省略 }
DAO层的修复和方案1一致。
补充说明
优先选择构造器注入的形式,这种方式能保证依赖不可变,也更方便编写单元测试,从根源上避免注入对象为null的问题。
内容的提问来源于stack exchange,提问作者karczi
相关产品推荐
相关产品推荐

