【问题标题】:Using dependency injection (via Guice) in a multi-module environment在多模块环境中使用依赖注入(通过 Guice)
【发布时间】:2017-03-19 04:18:13
【问题描述】:

在涉及 Guice 和避免非 Guice 单例时,我有一个小问题。考虑一个多模块项目,其中有 3 个模块:shared、frontend 和 backend。 frontend 和 backend 都在 shared 模块内使用 Profiling 类的实例(对方法进行计时,并在整个项目中广泛使用)。

几乎每个类都需要使用这个Profiling 实例(包括在用户连接时动态创建的User 对象)。

如果每个单独的类都需要Profiling 类的实例,那么不同的实现方法存在缺陷:

方案一(在构造函数中,复制到实例字段中):

private final Profiling profiling;

@Inject
public User(Profiling profiling, String username)

缺点:您必须在每个构造函数中包含一个Profiling 对象。这很麻烦,而且有点毫无意义。您还必须静态存储 Guice 的 Injector(或注入它),以便您可以即时创建 User 对象,而不仅仅是在首次加载程序时。

解决方案 2(仅作为实例字段):

@Inject
private Profiling profiling;

public User(String username)

缺点:与上面类似,您必须使用 Guice 的 Injector 来实例化每个对象。这意味着要动态创建User 对象,您需要在创建User 对象的类中的Injector 实例。

解决方案 3(作为一个(主)类中的静态字段,由我们手动创建)

public static final Profiling PROFILING; // Can also use a get method

public Application() {
    Application.PROFILING = injector.getInstance(Profiling.class)
}

缺点:违背 Guice 的/依赖注入建议 - 创建一个静态访问(通过 Application.PROFILING.start())的单例 Profiling 对象违背了 Guice 的目的?

解决方案 4(作为每个类中的静态字段,由 Guice 注入)

@Inject
private static Profiling profiling;

// You need to request every single class:
// requestStaticInjection(XXXX.class)

缺点:同样,这违背了 Guice 的/依赖注入建议,因为它是静态注入。我还必须请求 Guice 需要将 Profiler 注入的每个类(这也很麻烦)。

有没有更好的方法来设计我的项目并避免回到我过去使用的单例设计模式?

TL;DR:我希望能够跨每个类访问此 Profiling 实例(每个模块一个),而不会退回到单例设计模式。

谢谢!

【问题讨论】:

  • 我会按照您的建议(非静态)将它注入到任何 guice 管理的类中。我只会使用构造函数注入(因为它强制手动构造必须传递相同的参数并且不能创建无效实例)。但是,User 听起来不像是 guice 管理的类。我会(也许,这里没有足够的信息)通过一个可以注入 Profiling 类的 User-factory 来实现它,并抽象出每个用户都有一个分析实例的问题。

标签: java dependency-injection structure guice


【解决方案1】:

实际上,我会使用普通的单例,可能通过单字段枚举或类似模式。

要了解原因,您应该问一个问题:Guice 以及一般的依赖注入的目的是什么?目的是解耦应用程序的各个部分,以便它们可以独立开发和测试,以及集中配置和重新排列。考虑到这一点,您需要权衡 耦合成本 与 解耦成本。这取决于您选择耦合或分离的对象。

在这里,耦合的代价是,如果没有真正的 Profiling 工作实例,您将无法操作应用程序的任何部分,包括在测试中,包括对于像 User 之类的模型对象。因此,如果 Profiling 对其环境做出任何假设(例如,高分辨率系统定时调用的可用性),如果不禁用 Profiling,您将无法使用像 User 这样的类。此外,如果您希望您的测试使用新的(非单例)Profiling 实例进行分析以进行测试隔离,则需要单独实现它。但是,如果您的 Profiling 类足够轻量级,不会造成巨大的负担,那么您可能仍然会选择这种方式。

解耦的代价是它可以强制每个对象变成an injectable, as opposed to a newable。然后,您将能够在您的类和测试中替换 Profiling 的新/虚拟/假实现,并重新配置以在不同容器中使用不同的 Profiling 行为,但如果您没有理由替换,这种灵活性可能不会立即带来好处那些实现。对于稍后创建的 User 类,您需要追求工厂实现,例如通过 Guice assisted injection 或 AutoFactory code generation 提供的那些。 (请记住,您可以通过注入 Provider<T> 而不是 T 来创建任意数量的对象,而不是 T 注入工厂实例就像自定义 Provider 以采用您选择的 get 参数。 )

关于您的解决方案:

  • 解决方案 1 和 2,每个对象注入:这是工厂大放异彩的地方。 (如果可以选择,我更喜欢构造函数注入,所以我会在它们之间使用解决方案 1。)当然,创建新用户的所有内容都需要注入 User.Factory,因此这可能会变得很紧——将范围内的项目转换为将代码库中的每个类都转换为 DI 的项目——这对于您现在尝试做的事情来说可能是不可接受的成本。

    // Nested interface for your Factory:
    public interface Factory { User get(String username); }
    
    // Mark fields that the user provides:
    @Inject public User(Profiling profiling, @Assisted String username) { ... }
    
    // Wire up your Factory in a Module's configure:
    install(new FactoryModuleBuilder().implement(User.Factory.class));
    
    // Now you can create new Users on the fly:
    @Inject User.Factory userFactory;
    User myNewUser = userFactory.get("timothy");
    
  • 解决方案 3,请求静态注入 main holder 与我的想法差不多:对于不是通过依赖注入创建的对象,请求单个类的静态注入,例如 ProfilingHolder 或类似的.为了灵活性,你甚至可以给它无操作行为:

    public class ProfilingHolder {
      // Populate with requestStaticInjection(ProfilingHolder.class).
      @Inject static Profiling profilingInstance;
      private ProfilingHolder() { /* static access only */ }
      public static Profiling getInstance() {
        if (profilingInstance == null) {
          // Run without profiling in isolation and tests.
          return new NoOpProfilingInstance();
        }
        return profilingInstance;
      }
    }
    

    当然,如果您依赖于对 VM 单例的调用,那么您实际上是在接受普通的 VM 全局静态单例模式,只是在可能的情况下使用 Guice 进行交互。您可以轻松地扭转这种模式并拥有一个 Guice 模块 bind(Profiling.class).toInstance(Profiling.INSTANCE); 并获得相同的效果(假设 Profiling 可以在没有 Guice 的情况下实例化)。

  • 解决方案 4,每个类的 requestStaticInjection 是我唯一不会考虑的。类列表太长,它们变化的可能性Profiling 太少了。你会将一个模块变成一个高维护成本的购物清单,而不是任何有价值的配置,你会强迫自己打破封装或使用 Guice 进行测试。

所以,总而言之,我会为您当前的 injectable 对象选择 Guice 单例注入,为您当前的 newable 对象选择普通单例,以及迁移到工厂的选项如果/当您的任何 newables 跃升为 injectables。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-09
    相关资源
    最近更新 更多