如何使用Kotlin进行JEE EJB依赖注入

时间:2018-01-22 17:11:56

标签: dependency-injection kotlin ejb-3.1

我已经将正式用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或其他可以设置私有字段的模拟框架,这不是必需的。

我在实施时遇到了一些问题:

  1. 我必须将类和所有方法更改为open,以允许CDI容器为此bean创建代理。据我所知,没有其他方法可以让Weld在不允许子类化的情况下完成工作吗?
  2. 通常,Kotlin使用私有字段支持的给定修饰符(public / private / protected)为属性生成setter和getter。但是当使用lateinit时,生成的字段具有与getter和setter相同的可见性(Kotlin in Action,p.146)。我不明白这种特殊行为的背景。使用public属性会导致Weld报告中出现错误,即不允许使用公共字段。如何声明该字段应该是私有的,但是getter和setter是受保护的(在测试中初始化Resource)?
  3. 上面的代码中注释了什么?该字段还是生成的方法?如果是字段:我如何仅为注册器添加注释?
  4. 因为除了私有方法之外的所有方法都必须open所有属性都被Weld容器拒绝:Kotlin创建具有相同可见性的Getters和Setter,容器尝试代理bean代理所有方法豆子。这不起作用,因为生成的getter和setter不是open。对于私有属性,没有问题,因为容器不代理私有方法。我认为不可能将getter / setter声明为open,因此无法使用受保护的属性
  5. 编辑:添加了Ploblem 4,并因为这个问题将实施更改为私有设置者。

2 个答案:

答案 0 :(得分:1)

  1. 我也不知道背景,但你可以通过改变代码来解决这个问题,比如
  2. open class A { @Context private lateinit var _backing: SecurityContext open protected var field: SecurityContext get() = _backing set(value) { _backing = value } }

    此外,您可以使用构造函数注入

    1. 你在那里注释。要注释getter / setter,您可以将@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
}

请注意,主空构造函数必须仍然存在。

以下是我的问题的单独答案:

  1. 如果依赖注入框架使用代理open对于类以及每个方法和属性(但private个)都是必需的。
  2. 否。使用lateinit,该属性与getter和setter具有相同的可见性。如果要执行此操作,则必须将属性声明为可为空。但是,你必须在每次访问都使用!!时才会感到尴尬。
  3. 该领域。如上所示,您可以编写@Context set@set:Context来注释setter。
  4. 如1中所述,所有属性,但私有属性必须声明为开放。
  5. 编辑:请注意,私有属性也会导致问题(使用Wildfly测试)。我的建议是不要使用私有,而是保护。

    编辑2:请注意,这些解决方法仅在EJB(如REST资源)上是必需的。使用简单的CDI Beans,在它们被某个方面代理之前,这不是问题。那么这篇文章也适用。