如何注入另一个类加载器中存在的 CDI 托管 bean

How to inject a CDI managed bean that exists in another classloader

我正在开发一个将在 Java EE 应用程序中使用的框架,因此可能会部署在 EAR 文件的 \lib 目录中。

该框架将使用 CDI 以编程方式查找和注入位于使用该框架的 Java EE 应用程序中的 bean。我遇到的问题是,当我的框架调用 javax.enterprise.Provider<T> 中的 Provider.get() 方法来获取 bean 实例时,Weld 会抛出一个 UnsatisfiedResolutionException.

为了检查这不是与 CDI 相关的问题,我还尝试使用 MyClass myClass = Class.forName(clazz).newInstance(); 获取 class 的实例,但抛出了 ClassNotFoundException

我用于测试目的的 EAR 文件的结构如下:

MyTestApp.ear
+\lib\MyFramework.jar <----Contains the framework invoking the Provider.get() method
+MyTestApp.jar        <----Contains the bean I want to inject

我的测试应用程序的 EAR 包含一个 application.xml 文件,其中包含 <library-directory>lib</library-directory>.

我认为出现此问题是因为我要注入的 bean 存在于单独的 classloader 中。即 \lib\MyFramework.jarMyTestApp.jar 在不同的 classloader 中。我发现这个 SO question 似乎表明是这种情况。鉴于我正在开发一个框架,我认为问题的答案不是满足我需求的可行解决方案。

我很想知道创建 CDI 可移植扩展是否能让我获得我想使用的 bean 的实例,但在这方面没有足够的经验。使用 @Observes ProcessAnnotatedType<T> 我可以在 EAR 文件中看到存在于 \lib 目录之外的 beans,包括我想以编程方式注入的 beans。

我的问题是:

  1. 由于 \lib\MyFramework.jarMyTestApp.jar 在不同的 classloader 中,我假设这个问题的发生是否正确?

  2. 我可以使用 CDI 做些什么来让我的框架在 EAR 文件的 \lib 目录中部署时进行 Provider.get() 方法调用以避免 Weld扔 UnsatisfiedResolutionException?

  3. 我可以在 CDI 之外做些什么来达到相同的结果吗?


更新

我现在尝试将 MyFramework.jar 移动到 EAR 文件的根目录,并且还在 application.xml 文件中包含 jar 模块,但是由于 CDI 不满足,容器无法启动应用程序依赖异常。当 MyFramework.jar 位于 \lib 目录中并且与我的问题中引用的 bean 不同时,可以注入异常中引用的 bean。

1 : 是

2 : 其实我不知道

3:是的,您必须了解 ear 类加载器层次结构,ear lib 目录中的 jar 是在 ear 级别加载的,因此在所有子类加载器中都可用(ear 中每个组件都有一个子类加载器)。

这意味着 MyFramework.jar 从 MyTestApp.jar ear child classloader 可见,但反之则为 false。

In java EE, which jars should I put in the library dir?

您可以:

  • 将 MyTestApp.jar 移动到 ear lib 目录(MyFramework.jar 可以在 lib 目录和引用 MyTestApp.jar 中或在 ear root 中)
  • 将 MyFramework.jar 移动到耳根并在其清单类路径中引用 MyTestApp.jar

Deployment of multiple, depended CDI jars in one EAR

在我看来,让您的 "framework" 取决于您的应用程序实现可能是一个糟糕的架构决策。也许应该是您的应用程序实现了一些框架级接口来实现您的目标。正如您拥有的或希望它工作的那样,建议您耳中的应用程序,实际上是任何应用程序,都可以通过为框架提供实现来影响您耳中所有其他应用程序的操作。这对我来说似乎是个坏主意。如果您能够完成这项工作,并且您耳边的多个应用程序提供了此实现,CDI 可能仍会因为不明确的依赖关系而失败。

您可能可以使用一些特定于容器的依赖项配置。例如,WildFly 允许您配置模块到模块的依赖关系。我认为,在 WildFly 中,耳朵可能会依赖于 war.

虽然上面的方法可能行得通,但在考虑之后,我认为这也是一个坏主意。你真的应该仔细看看你真正想做什么。我认为,如果你更仔细地审视你正在尝试做的事情,你会发现你可能能够去除或抽象一些依赖关系并仍然为框架提供实现,但不是从耳中的应用程序。