我已经将正式用Java编写的JEE应用程序中的REST资源转换为Kotlin。该应用程序在带有Weld作为依赖注入框架的Wildfly应用程序服务器中运行。
这是我提出的最终实施:
@Path("/myResource")
open class MyResource {
@Context
private lateinit var context: SecurityContext
open protected setSecurityContext(securityContext: SecurityContext) {
this.context = securityContext
}
@POST
@Path("/change")
@Transactional
@Consumes(MediaType.APPLICATION_JSON)
open internal fun change(data: MyChangeData, @Context uriInfo: UriInfo): Response {
// ...
}
}
setter用于测试目的。使用Mockito或其他可以设置私有字段的模拟框架,这不是必需的。
我在实施时遇到了一些问题:
open
,以允许CDI容器为此bean创建代理。据我所知,没有其他方法可以让Weld在不允许子类化的情况下完成工作吗?lateinit
时,生成的字段具有与getter和setter相同的可见性(Kotlin in Action,p.146)。我不明白这种特殊行为的背景。使用public
属性会导致Weld报告中出现错误,即不允许使用公共字段。如何声明该字段应该是私有的,但是getter和setter是受保护的(在测试中初始化Resource)?open
所有属性都被Weld容器拒绝:Kotlin创建具有相同可见性的Getters和Setter,容器尝试代理bean代理所有方法豆子。这不起作用,因为生成的getter和setter不是open
。对于私有属性,没有问题,因为容器不代理私有方法。我认为不可能将getter / setter声明为open
,因此无法使用受保护的属性编辑:添加了Ploblem 4,并因为这个问题将实施更改为私有设置者。
答案 0 :(得分:1)
open class A {
@Context
private lateinit var _backing: SecurityContext
open protected var field: SecurityContext
get() = _backing
set(value) { _backing = value }
}
此外,您可以使用构造函数注入
@get:
和@set:
添加到注释中。答案 1 :(得分:0)
我能找到的最佳解决方案是将属性声明为open protected
:
@Context
open protected lateinit var context: SecurityContext
这样容器可以覆盖它,并且java或groovy的测试会将setter视为包受保护。
如果您只想注释setter(我更喜欢但更详细),您可以使用:
open protected lateinit var context: SecurityContext @Context set
或更好更短:
@set:Context
open protected lateinit var context: SecurityContext
不幸的是,这不适用于Kotlin测试,因为protected
变量只能由Kotlin中的子类看到。在这里你必须写一个单独的访问者:
@Context
open protected lateinit var context: SecurityContext
open internal fun setTheSecurityContext(context: SecurityContext) ...
或者您可以使用辅助构造函数:
open class MyResource() {
constructor(context: SecurityContext): this() {
this.context = context
}
请注意,主空构造函数必须仍然存在。
以下是我的问题的单独答案:
open
对于类以及每个方法和属性(但private
个)都是必需的。lateinit
,该属性与getter和setter具有相同的可见性。如果要执行此操作,则必须将属性声明为可为空。但是,你必须在每次访问都使用!!
时才会感到尴尬。@Context set
或@set:Context
来注释setter。编辑:请注意,私有属性也会导致问题(使用Wildfly测试)。我的建议是不要使用私有,而是保护。
编辑2:请注意,这些解决方法仅在EJB(如REST资源)上是必需的。使用简单的CDI Beans,在它们被某个方面代理之前,这不是问题。那么这篇文章也适用。