【问题标题】:How to use two Guice modules that install a common dependency module如何使用安装公共依赖模块的两个 Guice 模块
【发布时间】:2016-02-24 18:21:16
【问题描述】:

我正在做一个由四个部分组成的项目:

  • Main 项目将所有内容结合在一起。这包含 public static void main(String... args) 入口点。
  • 组件A
  • 组件B
  • AB 都引用的第三方 Common 组件。

我将 Guice 用于所有四个部分之间的管道,这是我的问题:
As 和Bs 的主要Guice 模块中,我安装了一个扩展了Common 中定义的模块。在运行时,此设置失败并出现以下错误:

common.SomeClass 的绑定已在 common.AbstractCommonModule.configure() 配置。 [source]

原因是我调用了common.AbstractCommonModule.configure() 两次;一次通过从组件Acom.a.MainModule.configure() 安装common.AbstractCommonPrivateModule 的子类实例,第二次从组件Bcom.b.MainModule.configure() 安装。

一个Main中安装common.AbstractCommonPrivateModule的实例不是一个选项,因为AbstractCommonPrivateModule实现了一个特定的绑定方法bindComplicatedStuff(ComplicatedStuff),为此我只知道@987654349里面的参数@ 和B

我尝试通过将AB 各自的主要Guice 模块包装在PrivateModules 中来解决整个问题。但是,这失败了,出现下一个错误:

无法为 %s 创建绑定。它已经在一个或多个子注入器或私有模块上配置 %s%n 如果它在 PrivateModule 中,您是否忘记公开绑定? [source]

就我而言,AB 各自的主要 Guice 模块实际上是 ServletModules - 显然我可以Main 安装两次。

如何绕过这些错误并安装两次 AbstractCommonPrivateModule 模块?

编辑:我上传了一些示例代码(对一些细节进行了解释)to GitHub

【问题讨论】:

  • 这听起来像是一个有趣的问题,但如果没有代码示例,我很难完全理解它。你能发布(或链接一个要点)你到目前为止所尝试的吗?
  • common.SomeClass 绑定是什么样的?你也许可以用 Guice 自动为你删除重复数据的方式编写它。
  • 我添加了一些code on GitHub。抱歉耽搁了

标签: java guice


【解决方案1】:

与其让AB 安装Common,不如让它们requireBinding() 用于他们需要从Common 获得的类。然后依赖AB 的模块也需要安装Common。这可能感觉有点奇怪,但实际上是可取的,因为 AB 现在与 Common 的耦合不那么紧密了。


更新

我安装两个 ShiroWebModules 的原因是因为我希望 ui 模块中的 Jersey 资源仅使用一种 Shiro 配置(一种不支持密码保护资源的配置)来保护,而api 模块应使用完全不同的 Shiro 配置(仅将不记名令牌理解为身份验证机制的配置)进行保护。

从广义上讲,这是棘手的。 Guice Injector 为整个应用程序提供了一种做某事的方式(通常是接口的一种实现);每个包没有不同的机制。您的两个Modules、SwsApiServletModuleSwsUiServletModule 提供了许多相同的绑定,而SwsModule 将它们安装在一起。本质上,您是在说“Guice,请提供基于承载令牌的身份验证机制”,然后紧接着说“Guice,请提供基于密码的身份验证机制”。它只能做一个或另一个,所以它不会随意选择一个,而是快速失败。

当然,有多种解决方案,具体取决于您的具体需求。最常见的是使用binding annotations 并让 UI 和 API 代码请求不同的注解。这样你就可以安装同一个接口或类的两个不同的实现(带有不同的注解)。

这是一个例子:

package api;

public class ApiResources {
  @Inject
  public ApiResources(@ApiAuthMechanism AuthMechanism auth) {
    this.auth = auth;
  }
}

---

package api;

public class ApiModule implements Module {
  public void configure() {
    bind(AuthMechanism.class).annotatedWith(ApiAuthMechanism.class)
        .to(BearerTokenAuthMechanism.class);
  }
}

---

package ui;

public class UiResources {
  @Inject
  public UiResources(@UiAuthMechanism AuthMechanism auth) {
    this.auth = auth;
  }
}

---

package ui;

public class UiModule implements Module {
  public void configure() {
    bind(AuthMechanism.class).annotatedWith(UiAuthMechanism.class)
        .to(PasswordAuthMechanism.class);
  }
}

---

package webap;

public class WebappModule implements Module {
  public void configure() {
    // These modules can be installed together,
    // because they don't install overlapping bindings 
    install(new ApiModule());
    install(new UiModule());
  }
}

您在评论中提到您无法控制正在安装的重叠绑定,因为它们来自第三方模块。如果是这种情况(我没有看到您的代码中发生的情况),出于安全原因,第三方可能不希望您做您想做的事情。例如, 简单地绑定基于密码的机制可能会在整个应用程序中引入漏洞。可能值得尝试更好地了解第三方打算如何使用他们的模块。

另一种不太理想但适用于某些用例的选项是使用两个完全独立的Injector 实例,每个绑定一个。然后你手动将你需要的实例直接传递给 UI 和 API 代码。这在某种程度上违背了 Guice 的目的,但这并不总是错误的决定。使用child Injectors 可以减轻这种痛苦。


顺便说一句,您的“示例代码”非常庞大,可能超过 90% 与问题无关。将来请花时间创建一个SSCCE,其中仅包含与手头问题相关的代码。根本没有任何人会筛选 100 多个 Java 文件和 7,300 多行代码来理解您的问题。这不仅会使试图帮助您的人更容易,而且只需尝试创建一个展示问题的 SSCCE 通常就足以帮助您自己理解和解决问题。

【讨论】:

  • 我喜欢这个建议,通常这就是我要做的。但是,在我的例子中,common.SomeClass 绑定是由第三方库的Module 构建的。而且我不能简单地从我的Main 模块中安装该模块一次,因为它依赖于其他绑定,这些绑定在AB 的范围内是不同的。我添加了sample code to GitHub
  • 感谢 SSCCE。这个周末我会试着玩它。
  • 感谢您的回答。在您旁边注意:我意识到“SSCCE”中有很多辅助代码,但我想提供一个非人为的示例来演示使用我遇到问题的实际库(Shiro)的问题,并且需要一些辅助代码(可能比我的少)。但是,这也是我包含自述文件的原因
  • 我理解这个意图,但非人为示例的问题是仍然存在多个移动部件,其中一个或多个可能涉及。一个人为但有代表性的例子让我们能够理解整个问题,而我根本无法花时间去理解你发布的整个代码示例中发生了什么。
【解决方案2】:

要安装同一个模块两次,覆盖模块中的.equals 方法以引用类而不是对象相等。 Guice 不会安装与已经安装的模块相同的模块。在您键入时,这在大多数情况下都无济于事:

install new AbstractCommonPrivateModule();

所以每个对象都是一个不同的实例,不等于最后一个。覆盖equals 方法可以解决这个问题:

@Override
public boolean equals(Object obj) {
    return obj != null && this.getClass().equals(obj.getClass());
}

// Override hashCode as well.
@Override
public int hashCode() {
    return this.getClass().hashCode();
}

但是,请注意,这种方法通常是不正确的。

为什么不做上面的事情

此时,您并没有真正使用 Guice 或依赖注入。相反,您将AbstractCommonPrivateModule 的实现与安装它的BC 的实现紧密耦合。正如@dimo414 所提到的,这里的OP 似乎真的想使用两个不同的ShiroWebModules,这正是Guice 通过在更高级别安装这两个不同模块所擅长的。同样,更高级别的安装可让您在测试时换掉。如果你真的想在某个时候换掉其中一个模块,Guice 会再次崩溃。

如果您覆盖模块(这是另一个有用的测试工具),这也可能会中断。

OP 还想安装一个通用模块两次。包装另一个库的通用模块会增加额外的风险;原作者自己不实施上述技巧可能有很好的理由,例如安全性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-30
    • 1970-01-01
    • 2016-09-16
    • 2013-08-26
    • 1970-01-01
    • 2015-02-12
    相关资源
    最近更新 更多