如何用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或其他模拟框架,可以设置私人领域这不是必要的。
我在这个实现中遇到了一些问题:
- 我不得不改变类和所有
open
方法,以允许CDI容器为这个bean创建一个代理。 据我了解这个话题,没有其他办法可以让Weld在不允许inheritance的情况下完成工作吗? - 一般来说,Kotlin使用私有字段支持的给定修饰符(public / private / protected)为属性生成setter和getter。 但是在使用
lateinit
,字段的生成与getter和setter具有相同的可见性(Kotlin in Action,lateinit
)。 我不明白这个特殊行为的背景。 使用public
属性会导致焊接报告中的错误,即公共字段不被允许。 我怎样才能声明这个字段应该是私有的,但getter和setter是受保护的(在测试中初始化资源)? - 上面的代码中注释了什么? 该字段或生成的方法? 如果是现场:我怎样才能注释二传手?
- 因为所有的私有方法都必须
open
所有的属性,但是私有的方法被Weld容器拒绝:Kotlin创建了具有相同可见性的Getters和Setter,并且容器试图代理bean来代理bean的所有方法。 这是行不通的,因为生成的getter和setter没有open
。 对于私有属性,没有问题,因为容器不能代理私有方法。 正如我所看到的那样,没有可能将getter / setter声明为open
因此不可能使用受保护的属性
编辑:增加了Ploblem 4,并改变了实施,因为这个问题私人与二传手。
- 是
- 我也不知道背景,但是你可以通过改变代码的方式来解决这个问题
open class A { @Context private lateinit var _backing: SecurityContext open protected var field: SecurityContext get() = _backing set(value) { _backing = value } }
另外,你可以使用构造函数注入
- 你在那里注释领域。 要注释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测试,因为protected
variables只能在Kotlin的子类中看到。 在这里你必须写一个单独的访问器:
@Context open protected lateinit var context: SecurityContext open internal fun setTheSecurityContext(context: SecurityContext) ...
或者你可以使用辅助构造函数:
open class MyResource() { constructor(context: SecurityContext): this() { this.context = context }
请注意,主空构造函数必须仍然存在。
以下是我的问题单独的答案:
- 如果dependency injection框架使用proxys,对于类和每个方法和属性(但是
private
的)来说都是必需的。 - 没有。在
lateinit
这个属性和getter和setter具有相同的可见性。 如果你想这样做,你必须声明该属性为空。 但是,那么你必须使用!!
在每个访问是尴尬的。 - 场。 如上所示,您可以编写
@Context set
或@set:Context
来注释setter。 - 如1所述,所有的财产,但私人的财产必须宣布为公开。