如何用Kotlin做JEE CDIdependency injection

我已经将正式编写成Java的JEE应用程序中的REST资源转换为Kotlin。 该应用程序以Weld作为dependency injection框架在Wildfly Application Server中运行。

这是我提出的最终实现:

@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 { // ... } } 

二传手是为了测试的目的。 随着Mockito或其他模拟框架,可以设置私人领域这不是必要的。

我在这个实现中遇到了一些问题:

  1. 我不得不改变类和所有open方法,以允许CDI容器为这个bean创建一个代理。 据我了解这个话题,没有其他办法可以让Weld在不允许inheritance的情况下完成工作吗?
  2. 一般来说,Kotlin使用私有字段支持的给定修饰符(public / private / protected)为属性生成setter和getter。 但是在使用lateinit ,字段的生成与getter和setter具有相同的可见性(Kotlin in Action, lateinit )。 我不明白这个特殊行为的背景。 使用public属性会导致焊接报告中的错误,即公共字段不被允许。 我怎样才能声明这个字段应该是私有的,但getter和setter是受保护的(在测试中初始化资源)?
  3. 上面的代码中注释了什么? 该字段或生成的方法? 如果是现场:我怎样才能注释二传手?
  4. 因为所有的私有方法都必须open所有的属性,但是私有的方法被Weld容器拒绝:Kotlin创建了具有相同可见性的Getters和Setter,并且容器试图代理bean来代理bean的所有方法。 这是行不通的,因为生成的getter和setter没有open 。 对于私有属性,没有问题,因为容器不能代理私有方法。 正如我所看到的那样,没有可能将getter / setter声明为open因此不可能使用受保护的属性

编辑:增加了Ploblem 4,并改变了实施,因为这个问题私人与二传手。

  1. 我也不知道背景,但是你可以通过改变代码的方式来解决这个问题

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:添加到注释中。

我可以find的最好的解决方案是声明属性为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测试,因为protectedvariables只能在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. 如果dependency injection框架使用proxys,对于类和每个方法和属性(但是private的)来说都是必需的。
  2. 没有。在lateinit这个属性和getter和setter具有相同的可见性。 如果你想这样做,你必须声明该属性为空。 但是,那么你必须使用!! 在每个访问是尴尬的。
  3. 场。 如上所示,您可以编写@Context set@set:Context来注释setter。
  4. 如1所述,所有的财产,但私人的财产必须宣布为公开。