【问题标题】:Global injectors with Guice使用 Guice 的全局注入器
【发布时间】:2025-12-21 01:55:07
【问题描述】:

我正在创建一个库并帮助实现人体工程学,我将在应用程序启动时创建的注入器传递给库中的不同组件,以便用户可以在某些上下文中执行getInstance(),而无需预先计划在哪里粘贴他们的@Inject 注释。

以下是 API 设计示例:

public static void main(String[] args) {
  // Main injector is created here behind the scenes. 
  ApexApplication app = new ApexApplication()

  app.get("/users/:id", context -> {
    // Users can do this instead of @Inject UserDAO dao
    UserDAO dao = context.getInstance(UserDao.class)
    User user = dao.findUserById(context.param("id"))
    //... 
  })
  app.start();
}

为了清楚起见,这里是关键实现细节的链接:

  • #1:这里是我创建初始(也是唯一)注入器的地方
  • #2:然后我将它作为构造函数的一部分传递给另一个库组件
  • #3:然后另一个组件将对 getInstance() 的调用委托给原始注入器

我知道 Guice 注入器的最佳实践是在整个应用程序中创建一个且只有一个,使用它来注入所有依赖项,然后将其丢弃;但是,考虑到我想要实现的目标,是否会推荐这种方法,还是我应该研究另一种模式?

【问题讨论】:

  • 如果你在 Guice 模块中引入了一个类似 MyModuleHelper 的并行类,你可以避免绕过注入器,并且更明确地访问。在其中,填充通过在模块上调用 getInstance() 初始化的 public static final 字段(或助手类中的静态方法。)然后,直接从代码库周围使用这些常量字段,而不是传递注入器.
  • @Jameson 我不确定我是否遵循。可以举个例子吗?

标签: java dependency-injection guice


【解决方案1】:

我认为将带有注入器的上下文发送给其他所有类以便访问注入器不是一个好主意。

还有另一种方法,实际上与@Jameson 的 MyModuleHelper 的想法非常相似。 DIContainer 对注入器进行静态初始化,并提供公共静态 API getInjector()。这样你就可以拥有注入器,它只创建一次,并且可以用于任何没有样板代码的类:

public final class DIContainer {
     private static final Injector injector;
     static {
         injector = Guice.createInjector(new AppModule());
     } 
     public static Injector getInjector() {
         return injector;
     }
     private DIContainer() {
     }
}

使用示例:

ServiceProvider service = DIContainer.getInjector().getInstance(ServiceProvider.class);

可能this post 也很有用。

【讨论】:

  • 但这是一种安全的方法吗?该库基于多线程的 Vertx。
  • 这就是事情变得棘手的地方,因为用户应该能够指定一个配置模块,而不必自己创建这些类之一。 ApexApplication 的构造函数之一采用 AbstractModule
  • 按照我的方法DIContainer会封装配置。无需将配置发送到ApexApplication,因为它只需要注入器。
【解决方案2】:

我接受了@asch 的建议,但做了一些修改,以允许用户设置自己的配置模块。

public class DependencyManager {
    private static Injector injector;
    private static Logger logger = LoggerFactory.getLogger(DependencyManager.class);

    public static Injector initializeWith(ApexConfiguration configuration) {
        if(injector == null) {
            logger.debug("Initializing injector")
            injector = Guice.createInjector(configuration);
        }
        return getInjector();
    }

    public static Injector getInjector() {
        if(injector == null) {
            logger.error("Application hasn't been configured yet.");
            return null;
        }
        return injector;
    }

    private DependencyManager() {}
}

我不完全确定它的线程安全性,但在我所有测试的执行过程中,日志消息 Initializing injector 仅显示一次,并且在调用 getInjector() 时我从未收到 NullPointerException

我相信这是因为库的结构方式,DependencyManager 必须在任何其他应用程序操作发生之前自动初始化。

欢迎任何改进这一点的 cmets/建议。

【讨论】: