Spring Boot @Async场景下直接访问字段报NullPointerException的原因
问题分析:Spring Boot中直接访问public字段抛出NPE,调用getter方法正常的原因
相关代码
@Service public class Listener { public ConcurrentHashMap<String, String> bars; public Listener() { bars = new ConcurrentHashMap<>(); } public ConcurrentHashMap<String, String> getBars() { return bars; } @Async public void addBars(CountDownLatch counter) { bars.put("hello", "world"); counter.countDown(); } } @RestController public class Controller { @Autowired Listener listener; @GetMapping("/") public String trial() throws InterruptedException { CountDownLatch counter = new CountDownLatch(1); listener.addBars(counter); counter.await(); return listener.bars.get("hello"); // 直接访问字段会NPE,换成getBars()就正常 } }
原因解析
- Spring代理机制的影响:因为
Listener类包含@Async注解的方法,Spring会为该类生成代理对象(默认用CGLIB代理,因为Listener是普通类未实现接口),以此实现异步逻辑。 - 代理对象的字段未初始化:Spring创建CGLIB代理时,为了提升性能,会通过Objenesis工具跳过原类构造方法的执行来实例化代理对象。这导致代理对象自身的
bars字段没有被初始化(原类构造里的bars = new ConcurrentHashMap<>()未执行),直接访问listener.bars时,拿到的是代理对象中未初始化的null字段,因此抛出NullPointerException。 - getter方法的委托逻辑:调用
getBars()方法时,由于该方法没有被@Async标注,代理对象会把方法调用委托给真正的Listener目标实例(这个实例的构造方法已正常执行,bars字段已初始化),所以能返回正确的ConcurrentHashMap实例,不会出现NPE。
内容的提问来源于stack exchange,提问作者NewbieToCoding
相关产品推荐
相关产品推荐

