如何使用 Kotlin 进行 JEE EJB 依赖注入
How to do JEE EJB Dependency Injection with Kotlin
我已经将正式用 Java 编写的 JEE 应用程序中的 REST 资源转换为 Kotlin。该应用程序在 Wildfly 应用服务器中运行,使用 Weld 作为依赖注入框架。
这是我想出的最终实现:
@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 或其他可以设置私有字段的模拟框架,这不是必需的。
我在这个实现中遇到了一些问题:
- 我必须将 class 和所有方法更改为
open
以允许 CDI 容器为此 bean 创建代理。据我了解这个主题,没有其他方法可以让 Weld 在不允许 subclassing? 的情况下完成它的工作
- 通常,Kotlin 为具有给定修饰符 (public/private/protected) 的属性生成 setters 和 getters,并由私有字段支持。但是当使用
lateinit
时,生成的字段具有与 getters 和 setters 相同的可见性(Kotlin in Action,第 146 页)。我不明白这种特殊行为的背景。使用 public
属性 会导致 Weld 报告错误,即不允许 public 字段。我如何声明该字段应该是私有的,但 getter 和 setter 受保护(以在测试中初始化资源)?
- 上面的代码注释了什么?字段还是生成的方法?如果是字段:如何只注释setter?
- 因为除私有方法之外的所有方法都必须是
open
所有属性,但私有方法都被 Weld 容器拒绝:Kotlin 创建具有相同可见性的 Getters 和 Setters,并且容器尝试代理代理所有的 bean bean 的方法。这不起作用,因为生成的 getter 和 setter 不是 open
。对于私有属性,根本没有问题,因为容器不代理私有方法。正如我所见,无法将 getter/setter 声明为 open
因此无法使用受保护的属性
编辑:添加了问题 4 并由于此问题将实现更改为私有 setter。
- 是
- 我也不知道背景,但你可以通过将代码更改为 smth 来解决这个问题
open class A {
@Context
private lateinit var _backing: SecurityContext
open protected var field: SecurityContext
get() = _backing
set(value) { _backing = value }
}
此外,您可以使用构造函数注入
- 您正在那里注释字段。要注释 getters/setter,您可以将
@get:
和 @set:
添加到注释中。
我能找到的最佳解决方案是将 属性 声明为 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 中的 subclasses 看到。在这里你必须写一个单独的访问器:
@Context
open protected lateinit var context: SecurityContext
open internal fun setTheSecurityContext(context: SecurityContext) ...
或者您可以使用辅助构造函数:
open class MyResource() {
constructor(context: SecurityContext): this() {
this.context = context
}
请注意,主要的空构造函数必须仍然存在。
下面是我的问题的单独答案:
- 如果依赖注入框架使用代理
open
对于 class 和每个方法以及 属性(但 private
是必需的)。
- 没有。对于
lateinit
,属性 与吸气剂和 setter 具有相同的可见性。如果要执行此操作,则必须将 属性 声明为可为空。但是你必须在每次访问时使用 !!
,这很尴尬。
- 字段。如上所示,您可以编写
@Context set
或 @set:Context
来注释 setter.
- 如 1 中所述,所有属性(私有属性除外)都必须声明为开放。
编辑:请注意私有属性也会导致问题(使用 Wildfly 测试)。我的建议是根本不要使用 private,而是使用 protected。
编辑 2:请注意,这些解决方法仅在 EJB(如 REST 资源)上是必需的。对于简单的 CDI Beans,这不是问题,直到它们被某个方面等代理为止。那么这个post也适用。
我已经将正式用 Java 编写的 JEE 应用程序中的 REST 资源转换为 Kotlin。该应用程序在 Wildfly 应用服务器中运行,使用 Weld 作为依赖注入框架。
这是我想出的最终实现:
@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 或其他可以设置私有字段的模拟框架,这不是必需的。
我在这个实现中遇到了一些问题:
- 我必须将 class 和所有方法更改为
open
以允许 CDI 容器为此 bean 创建代理。据我了解这个主题,没有其他方法可以让 Weld 在不允许 subclassing? 的情况下完成它的工作
- 通常,Kotlin 为具有给定修饰符 (public/private/protected) 的属性生成 setters 和 getters,并由私有字段支持。但是当使用
lateinit
时,生成的字段具有与 getters 和 setters 相同的可见性(Kotlin in Action,第 146 页)。我不明白这种特殊行为的背景。使用public
属性 会导致 Weld 报告错误,即不允许 public 字段。我如何声明该字段应该是私有的,但 getter 和 setter 受保护(以在测试中初始化资源)? - 上面的代码注释了什么?字段还是生成的方法?如果是字段:如何只注释setter?
- 因为除私有方法之外的所有方法都必须是
open
所有属性,但私有方法都被 Weld 容器拒绝:Kotlin 创建具有相同可见性的 Getters 和 Setters,并且容器尝试代理代理所有的 bean bean 的方法。这不起作用,因为生成的 getter 和 setter 不是open
。对于私有属性,根本没有问题,因为容器不代理私有方法。正如我所见,无法将 getter/setter 声明为open
因此无法使用受保护的属性
编辑:添加了问题 4 并由于此问题将实现更改为私有 setter。
- 是
- 我也不知道背景,但你可以通过将代码更改为 smth 来解决这个问题
open class A {
@Context
private lateinit var _backing: SecurityContext
open protected var field: SecurityContext
get() = _backing
set(value) { _backing = value }
}
此外,您可以使用构造函数注入
- 您正在那里注释字段。要注释 getters/setter,您可以将
@get:
和@set:
添加到注释中。
我能找到的最佳解决方案是将 属性 声明为 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 中的 subclasses 看到。在这里你必须写一个单独的访问器:
@Context
open protected lateinit var context: SecurityContext
open internal fun setTheSecurityContext(context: SecurityContext) ...
或者您可以使用辅助构造函数:
open class MyResource() {
constructor(context: SecurityContext): this() {
this.context = context
}
请注意,主要的空构造函数必须仍然存在。
下面是我的问题的单独答案:
- 如果依赖注入框架使用代理
open
对于 class 和每个方法以及 属性(但private
是必需的)。 - 没有。对于
lateinit
,属性 与吸气剂和 setter 具有相同的可见性。如果要执行此操作,则必须将 属性 声明为可为空。但是你必须在每次访问时使用!!
,这很尴尬。 - 字段。如上所示,您可以编写
@Context set
或@set:Context
来注释 setter. - 如 1 中所述,所有属性(私有属性除外)都必须声明为开放。
编辑:请注意私有属性也会导致问题(使用 Wildfly 测试)。我的建议是根本不要使用 private,而是使用 protected。
编辑 2:请注意,这些解决方法仅在 EJB(如 REST 资源)上是必需的。对于简单的 CDI Beans,这不是问题,直到它们被某个方面等代理为止。那么这个post也适用。