【问题标题】:Guice - Field Injection limitationsGuice - 现场注入限制
【发布时间】:2014-03-12 21:30:25
【问题描述】:

假设我们有一个名为 Bean 的类,它依赖于另一个名为 Service 的类,使用 Fields Injection 并且 我们无法更改这些类的代码。另一个类 (App) 使用 Provider 来随时构造 Bean 的新实例,我们可以随意更改这些类中的代码。

由于在Providerget() 方法中,新实例是使用new 运算符创建的,因此无法将Service 注入Bean,因此Bean 实例将不完整(服务字段为null)。

绕过该问题的一种方法是在 Provider 中注入 Injector 并在新实例上的 get() 方法中调用 injector.injectMembers()

代码示例如下(我避免使用接口来缩短代码)。

class Bean {
    @Inject private Service service;

    public Bean() {
    }

    public void bar() {
        this.service.foo();
    }
}

class Service {

    public void foo() {
        System.out.println("foo");
    }
}

class App {
    @Inject private Provider<Bean> beanProvider;

    public App() {
    }

    public void run() {
        this.beanProvider.get().bar();
    }
}

class BeanProvider implements Provider<Bean> {
    @Inject Injector injector;

    public Bean get() {
        Bean mybean = new Bean();
        this.injector.injectMembers(mybean);

        return mybean;
    }
}

// A simple module to bind the classes
class MyModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(Service.class);
        bind(Bean.class).toProvider(BeanProvider.class);
        bind(App.class);
    }
}


public static void main(String[] args) {
    Injector injector = Guice.createInjector(new MyModule());
    injector.getInstance(App.class).run();
}

另一种方法是将 Bean 更改为使用构造注入,但正如我所说,让我们假设我们不能这样做。

我知道大多数人声称应该避免注入 Injector,因为它破坏了整个 DI 理念,因为注入器应该只在应用程序的入口点使用,而不能在其他任何地方使用,但是因为 Guice 提供了不同的方法来在应用程序中使用 DI,例如 Field Injection,可能会发生我描述的情况。此外,我已经看到在几个项目中经常使用字段注入,因此经常会出现这种情况。

那么,有没有一种“更干净”的方法来避免在我描述的问题中注入Injector

【问题讨论】:

  • 在您的示例中,您实际上并不需要BeanProvider。 Guice 将为您创建一个。所以你可以在没有提供者的情况下只使用bind(Bean.class),你的例子就可以了。还有其他限制吗?
  • @condit Bean 的范围比 App 更窄的情况呢? guice (code.google.com/p/google-guice/wiki/InjectingProviders) 的文档建议使用 Provider 来处理此类情况。无论如何,让我们假设使用 Provider 是强制性的。我还需要注射注射器吗?
  • 即使这样,Guice 也会自动为您注入一个提供程序,这应该可以解决您的范围问题(因为每个提供程序 get 调用都会返回一个新的 Bean)。我发现自己唯一一次使用 requestInjection 是为了 AOP。

标签: java dependency-injection guice guice-3


【解决方案1】:

这似乎与您的模块无关。看起来您根本不需要创建提供程序。

您在模块中设置的绑定应该会导致 Service 在您需要的任何地方注入到 Bean 中。因此,您应该能够将 App 中的提供者声明替换为 Bean 注入。

class App {
     @Inject private Bean beanObject;

     public App() {
     }

     public void run() {
         this.beanObject.bar();
     }
}

// A simple module to bind the classes
class MyModule extends AbstractModule {
    @Override
    protected void configure() {
        //bind this to whatever you want to inject into Bean...
        bind(Service.class); 
        bind(Bean.class);
        bind(App.class);
    }
}

如果这对您不起作用,您能否说明您正在尝试做什么以及为什么需要提供者?

【讨论】:

  • 感谢您的回答。我想使用 Provider 而不是简单注入的原因是因为在某些情况下 Bean 类的范围可能比 App 类更窄(现在可能名称令人困惑,但问题的重点需要,出于某种原因,使用提供者)
  • 嗯...如果需要使用提供程序,那么我看不出有什么办法可以解决注入器的问题...(没有编写自己的反射bean填充器,并重新实现注入器本身的行为)。
  • @Alex,您注入的内容(直接的对象或提供者)根本不依赖于绑定的类型(链接到实例、提供者等等)。其实对于每一个绑定Guice都会生成一个provider,你可以在Guice源码中自己找到。因此,除非您真的需要提供者(例如,对于不适合 DI 的类),否则您可以使用普通链接绑定。
  • @Alex,对于您的两个问题,答案都是:注入提供者。但这并不意味着您必须绑定任何提供者。您可以创建简单的链接绑定并仍然注入提供程序。亲眼看看:gist.github.com/dpx-infinity/9529941。我没有创建任何 toProvider() 绑定,但我仍然能够注入一个提供程序,该提供程序在每次被要求时都会创建新实例。
  • 好吧,那你别无选择。要么使用注入器执行injectMembers,要么将所需的依赖项注入您的提供程序,然后手动将它们传递给提供的对象。如果可能的话,我会选择第二个选项,因为injectMembers 太含蓄了。但这与您最初的问题并没有真正的关系。在那个问题中,您询问了如何解决该特定问题,事实证明绝对有可能避免使用 Injector 甚至在这种情况下使用提供程序。我认为您应该就更通用的情况提出单独的问题。
猜你喜欢
  • 1970-01-01
  • 2011-05-13
  • 1970-01-01
  • 2012-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-28
  • 2016-09-04
相关资源
最近更新 更多