Spring 代理类和 Kotlin 中的空指针异常
Posted
技术标签:
【中文标题】Spring 代理类和 Kotlin 中的空指针异常【英文标题】:Null Pointer Exception In Spring Proxy Class and Kotlin 【发布时间】:2016-09-22 17:51:15 【问题描述】:我在将 kotlin 与 spring 结合使用时遇到了一些问题。
我有一个控制器 bean(顺便说一句没有接口),它通过主构造函数有一个自动连接的服务 bean。
除非我为控制器使用缓存注释,否则它工作得很好。显然 springs 缓存会在后台生成一个代理类来处理缓存。
我的代码如下所示:
@RestController
@RequestMapping("/regions/")
open class RegionController @Autowired constructor(val service: RegionService)
@RequestMapping("id", method = arrayOf(RequestMethod.GET))
@Cacheable(cacheNames = arrayOf("regions"))
fun get(@PathVariable id: Long): RegionResource
return this.service.get(id)
现在的问题是执行方法时出现空指针异常,实际上this.service
是null
,这在技术上是不可能的,因为它是 kotlin 中的非空变量。
我假设class proxies generated by spring 使用空值而不是自动装配的 bean 来初始化类。这一定是使用 kotlin 和 spring 的常见陷阱。你是如何规避这个问题的?
【问题讨论】:
【参考方案1】:在 Kotlin 中默认为 classes and members are final。
为了让代理库(CGLIB、javaassist)能够代理方法,它必须声明为 non final 并在 non final 类中(since those libraries implement proxying by subclassing)。将您的控制器方法更改为:
@RequestMapping("id", method = arrayOf(RequestMethod.GET))
@Cacheable(cacheNames = arrayOf("regions"))
open fun get(@PathVariable id: Long): RegionResource
return this.service.get(id)
您可能会在控制台中看到关于 RegionController
方法不受代理的警告。
The Kotlin compiler plugin
Kotlin 团队已经认识到这一困难,并创建了一个插件来标记标准 AOP 代理候选者,例如@Component
和 open
。
您可以通过build.gradle
启用插件:
plugins
id "org.jetbrains.kotlin.plugin.spring" version "1.1.60"
【讨论】:
这里值得一提:如果你有一个抽象类被一个带有@Component
注解的类扩展,spring的kotlin插件默认不会打开抽象函数。子进程可以毫无问题地调用父进程,但如果外部调用父方法,它将查看所有空成员变量。在方法中添加open
修复,或者在抽象类中添加spring注解,或者新建注解添加到kotlin-allopen
插件中【参考方案2】:
很快这可能不再是问题了。
正在进行的工作是任何库(例如包括 spring)都可以在 META-INF 中指定一个文件的注释列表。一旦一个类被其中之一注释,它将默认为该类本身及其所有功能打开。对于从带注释的类继承的类也是如此。
更多详情请关注https://github.com/Kotlin/KEEP/pull/40#issuecomment-250773204
【讨论】:
与此同时,kotlin 1.6 与新的全开放编译器插件一起发布:blog.jetbrains.com/kotlin/2016/12/kotlin-1-0-6-is-here以上是关于Spring 代理类和 Kotlin 中的空指针异常的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin空安全 ① ( Kotlin 的空安全机制 | 变量可空性 | 默认变量不可赋空值 | 声明可空类型变量 )