我写过几篇关于使用@Alternative实现部署时多态性的文章。但我从未真正讨论过它在CDI模块化中的作用。
模块化和替代方案
从我们的角度来看,对依赖管理中“模块化”的支持意味着不同模块中相同类型的注入点可以解析为不同的Bean。例如,假设我有一个接口Supplier,有两个实现。让我们假设它们是像BookSupplier和CameraSupplier这样的Bean,但它们也可能是更奇特的东西。现在,在我的应用的一个模块bookshop.war中,我有一个BookShop:
class BookShop {
@Inject
BookShop(Supplier supplier) { ... }
...
}
在另一个模块camerashop.war中,我有一个CameraShop:
class CameraShop {
@Inject
CameraShop(Supplier supplier) { ... }
...
}
现在,容器是如何知道SupplierinBookShop是BookSupplier,但SupplierinCameraShop是CameraSupplier?
Well, there's two possibilities
BookSupplier和CameraSupplierare deployed in different modules
假设BookSupplier和CameraSupplierare deployed in separate modules,books.jar和cameras.jarrespectively. Then the solution is easy. Just declare thatbookshop.wardepends onbooks.jarand thatcamerashop.warhas a dependency tocameras.jar, and we're done. That's enough information for CDI to know what to inject where.
Sobookshop.warwould contain the following line inMANIFEST.MF:
Class-Path: books.jar
Meanwhile,camerashop.warwould contain this line in itsMANIFEST.MF:
Class-Path: cameras.jar
BookSupplier和CameraSupplierare deployed in the same module
But what ifBookSupplier和CameraSupplierare both deployed insuppliers.jar? Well, there's a solution in this case, too.
First we need to add the@Alternativeannotation toBookSupplier和CameraSupplier. @Alternativetells CDI not to inject a bean into a module that does not explicitly declare its dependency to the bean.
接下来,我们需要在每个模块中声明适当的备选方案。bookshop.war中,我们指定
<beans>
<alternatives>
<class>org.mydomain.supplier.BookSupplier</class>
</alternatives>
</beans>
在camerashop.war中,我们指定
<beans>
<alternatives>
<class>org.mydomain.supplier.CameraSupplier</class>
</alternatives>
</beans>
现在每个模块都有自己的、私有的实现方式:Supplier.
如果BookShop和CameraShop被部署在同一个模块中呢?
我想,此时此刻,你可能会想知道,如果客户端BookShop和CameraShop被部署在同一个模块中会发生什么。好吧,在这种情况下,我们需要寻找另一种解决方案。CDI不支持更细粒度的备选方案激活。没有bean级别或包级别的备选方案。(至少在这个CDI版本的发布中不是。)
相反,我们需要添加一个限定符,例如
class BookShop {
@Inject
BookShop(@Supplies(Book.class) Supplier supplier) { ... }
...
}
或者更有可能的是,给Supplier:
class BookShop {
@Inject
BookShop(Supplier<Book> supplier) { ... }
...
}
统一表达式语言(EL)
我们之前工作的例子涉及使用注入来获取依赖的Java类。但JSP或JSF页面怎么办?它们使用统一表达式语言(EL)表达式与bean交互。很高兴地告诉你,在EL名称解析期间,正好适用相同的规则。
Java EE之外的模块化
我给出了Java EE环境的例子。在这个环境中,一个“模块”是一个Java EE模块,如war、EJB jar或rar,或者是一个库jar。但其他模块架构,如OSGi或未来Java语言级别的模块系统怎么办呢?
在这方面,CDI规范略有前瞻性。这个功能是为了一个抽象的“模块”概念在抽象的“模块架构”中定义的。因此,在OSGi架构中,CDI实现应该做什么,如果它想要支持OSGi,这基本上是明确定义的。