一、问题描述
最近一个老项目线上出现一个问题,偶发性的出现mongo入库没有入成功,查看日志,得到如下报错:
java.lang.IllegalStateException: Expected lazy evaluation to yield a non-null value but got null!
at org.springframework.data.util.Lazy.get(Lazy.java:97)
at org.springframework.data.mapping.model.AnnotationBasedPersistentProperty.isWritable(AnnotationBasedPersistentProperty.java:211)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeProperties(MappingMongoConverter.java:533)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:524)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:497)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeCollectionInternal(MappingMongoConverter.java:744)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.createCollection(MappingMongoConverter.java:660)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:574)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeProperties(MappingMongoConverter.java:548)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:524)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:497)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.write(MappingMongoConverter.java:441)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.write(MappingMongoConverter.java:78)
at org.springframework.data.mongodb.core.EntityOperations$MappedEntity.toMappedDocument(EntityOperations.java:509)
at org.springframework.data.mongodb.core.MongoTemplate.doInsert(MongoTemplate.java:1234)
at org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:1171)
二、问题排查
首先找到报错的那行代码 Lazy
类的 get
方法的97行
这里的value返回了null,继续看 getNullable()
方法
正常情况应该返回的是 supplier.get()
的返回值,在 AnnotationBasedPersistentProperty
类的 isWritable
,可以看到这个 supplier
相当于一个懒加载的方式,如果正常走到 value = supplier.get();
是不可能返回 null
的,所以一定是 resolved
是 true
,导致走到了if语句里,返回了this.value
,我们看下这几个变量的初始化值
虽然 value
初始化是 null
,但是 resolved
是 false
分析下这里代码的用处,通过 resolved
来做一个懒加载的标记,一开始 resolved
是 false
,代码会跳过if,给 value
赋值,然后把 resolved
修改成 true
,下次再获取时 resolved
是 true
,可以直接获取到赋过值的 value
,从而达到懒加载的目的,看似没有什么问题,但是在多线程环境下,就有问题了,结合项目里,mongo插入确实是在线程池里操作的。
所以猜测这是个 spring
的 bug
,并且这个项目是个老项目,spring的版本较低,猜测估计在新版本中已经修复,去看了下新版本的代码
果然在新版本中 resolved
使用了 volatile
修饰,具体可以参考 https://github.com/spring-projects/spring-data-commons/commit/a442b9002181fa6461490d105a28dd187dd3c38d
三、解决问题
升级 spring 的版本