什么时候在Spring Boot应用程序的生命周期中Kotlin内省可用?
我遇到了一个令人惊讶的错误。 我正在尝试使用存储库模式访问mongodb的应用程序。 为了减少代码重复,我想为所有的存储库建立一个通用的基类。 每个根集合(如下面的代码中的Person
)的RepositoryBase
将从此RepositoryBase
inheritance,并inheritance所有常用function。
data class Person(val name: String) open class RepositoryBase (val template: ReactiveMongoTemplate, private val klass: KClass) { fun count(): Mono = template.count(Query(), klass.java) } @Repository class PersonRepository(template: ReactiveMongoTemplate): RepositoryBase(template, Person::class) @RunWith(SpringRunner::class) @SpringBootTest class DemoApplicationTests { @Autowired var personRepository: PersonRepository? = null @Test fun contextLoads() { println(personRepository?.count()?.block()!!) } }
但是,这似乎并不奏效:
java.lang.IllegalArgumentException:指定为非null的参数为null:方法kotlin.jvm.JvmClassMappingKt.getJavaClass,参数$ receiver
在kotlin.jvm.JvmClassMappingKt.getJavaClass(JvmClassMapping.kt)在com.example.demo.RepositoryBase.count(DemoApplicationTests.kt:18)…
看来在Person::class
被调用的时候,内省function并没有被完全初始化,后来对KClass.java
调用被定义为:
/** * Returns a Java [Class] instance corresponding to the given [KClass] instance. */ @Suppress("UPPER_BOUND_VIOLATED") public val KClass.java: Class @JvmName("getJavaClass") get() = (this as ClassBasedDeclarationContainer).jClass as Class
导致null-exception。
我想知道在Spring应用程序中是否使用自省的一些规则,或者这是否是在Kotlin或Spring中的错误。
TL; DR;
它看起来像一个错误,但它不是 – 这是事情如何工作的结果。
说明
这里发生什么是private val klass: KClass
是null
。 看代码,这实际上不会发生,但它确实。 在幕后发生的是Spring为PersonRepository
创建一个代理:
this = {com.example.demo.DemoApplicationTests@5173} personRepository = {com.example.demo.PersonRepository$$EnhancerBySpringCGLIB$$42849208@5193} "com.example.demo.PersonRepository@1961d75a" CGLIB$BOUND = false CGLIB$CALLBACK_0 = {org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor@5208} CGLIB$CALLBACK_1 = {org.springframework.aop.framework.CglibAopProxy$StaticUnadvisedInterceptor@5209} CGLIB$CALLBACK_2 = {org.springframework.aop.framework.CglibAopProxy$SerializableNoOp@5210} CGLIB$CALLBACK_3 = {org.springframework.aop.framework.CglibAopProxy$StaticDispatcher@5211} CGLIB$CALLBACK_4 = {org.springframework.aop.framework.CglibAopProxy$AdvisedDispatcher@5212} CGLIB$CALLBACK_5 = {org.springframework.aop.framework.CglibAopProxy$EqualsInterceptor@5213} CGLIB$CALLBACK_6 = {org.springframework.aop.framework.CglibAopProxy$HashCodeInterceptor@5214} template = null klass = null
正如你所看到的, klass
是null
。 这是一个重要的事实,因为你调用RepositoryBase.count()
。 count()
是final
,因此它不能被CGLIB代理。 在count()
内部,你正在访问klass
字段(这里不使用getter),因此调用使用来自代理实例的未初始化字段而不是实际目标。 使用getter方法会将调用路由到实际目标并检索字段。
解
让你的方法不是final
:
open class RepositoryBase (val template: ReactiveMongoTemplate, private val klass: KClass) { open fun count(): … }