通过注解动态生成注入

Dynamically generating injections by annotation

假设我有一个看起来像这样的 class:

public class MyClass {
    @Inject
    public MyClass(@Foo("whatever") Bar dependency) {
        // ...
    }
}

我想要一些自定义逻辑,可以看到我们正在注入一个类型为 Bar 的对象,并带有一个类型为 @Foo("whatever") 的注释,并构建一个相应的 Bar 对象......类似a Guice Provider,但它获得了有关注射部位的更多上下文信息。 Guice 让我做那样的事吗?

您所描述的内容无法通过普通 Guice 实现:提供程序旨在成为零参数纯函数,并且无法像灵活的回调函数那样将注入站点信息插入其中。

不过,您可以通过两种不同的方式大致了解您想要的内容:

  • 如果你知道每个 @Foo参数的可能值,你可以让你的@Foo成为binding annotation and bind it by providing a Annotation-compatible equals and hashCode.这提供了最直观的体验:你可以用你的 @Foo 做任何你可以用任何其他类型做的事情,比如在构造函数中使用 @Foo 或注入 @Foo("value") Provider<Bar> barProvider.

    @Override public void configure() {
      for (String value : PREDEFINED_VALUES) {
        bind(Bar.class)
            .annotatedWith(new FooImpl(value))
            .toProvider(new BarProvider(value));
      }
    }
    
  • 如果您希望 @Foo 适用于任意参数,您需要使用 custom injections 扩展 Guice。这不适用于构造函数注入或与任何其他 @Inject 注释一起使用,但它将允许您在 Guice 注入完成后检查类型以根据需要增加它们(例如检测并响应 @Foo字段注释)。

    请参阅 the example in the Guice docs 了解更多信息。

在内部,Guice 的核心实际上是一个 Map<Key, Provider>,其中一个 Key 表示一对可能参数化的类型和一个可选的绑定注释。前一个绑定注释技巧之所以有效,是因为 Guice 可以将您的注入请求映射到一个提供者,而后者跳过 Guice 的映射,因此您可以自己 inspect/construct/inject 个实例。


如果您愿意跳过解决方案的注释部分,您可以注入 BarProviderBarFactory 公开 forFoo(String) 方法,这将为您提供一致的注入无需提前知道所有 String 值。这将允许您使用 assisted injection or AutoFactory 来生成您的工厂(如果您希望每次调用生成一个实例),或者让您自己编写一个简单的工厂以增加灵活性。

public class MyClass {
    private final Bar dependency;

    @Inject
    public MyClass(BarProvider barProvider) {
        dependency = barProvider.forFoo("whatever");
        // ...
    }
}