如何注入依赖?

32

情境:我需要在一些FooClass中进行惰性依赖项实例化,因此我将Injector作为构造函数参数传递给类。

private final Injector m_injector;
    
public FooClass(@Named("FooInjector") Injector injector) {
    m_injector = injector;
}

但是guice不允许绑定核心类(注入器,模块等)。有什么解决方法吗?

4个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
29

你不应该直接使用 Injector。 相反,应该传递 Provider<FooClass>。此外,在使用FooClass的地方应该注入这个提供者。

private final Provider<FooClass> provider;

@Inject
public ClassWhereFooIsUsed(Provider<FooClass> provider) {
    this.provider = provider;
}

.... somewhere else
FooClass f = provider.get(); // This is lazy

谢谢!我会尝试使用懒加载。但是真的没有办法注入注射器吗? - Alex M
10
您可以通过@Inject Injector injector获取注入器,可以放在构造函数/字段/方法中。 - gpampara
2
@AlexM:如果你真的需要,可以看一下childInjectors。通常情况下,它们是不必要的,你应该只使用一个注入器。当然也有例外,但这种情况很少见。 - gpampara
2
为什么需要多个注入器?我真的无法想象。 - ColinD
@Alex M - 没有必要配置一个注入器,使注入器本身受限。默认情况下,每个注入器都知道如何注入自己。http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/Injector.html - Itay Maman
显示剩余2条评论

15

如其他人已经回答的那样,你可以简单地使用@Inject Injector,因为Guice定义了该绑定。

通常情况下,您的应用程序只需要一个Injector,而静态变量比注入更容易存储和访问单例。在我们的Web应用程序中,我们使用stripes-guicer并从其静态方法GuiceInjectorFactory.getInjector()获取Injector,当我们需要它时(例如在我们的Hibernate拦截器中)。

我有点困惑于“您不应该直接使用Injector”的建议。除了调用injector.getInstance()injector.injectMembers()之外,我怎么能注入实例呢?没有别的办法。是的,您可以定义Provider方法,但除非某个地方,某些东西使用了Injector,否则这些方法将永远不会被调用。是的,有一些模块会为您使用Injector,例如ServletModule;您必须自己创建Injector,但之后可以交给ServletModule处理。

因此,在某些情况下,您可以避免直接使用Injector,但这并不意味着您“不应该”使用它。如果您仅使用Guice而没有任何可选模块,则应在许多地方使用Injector,因为没有其他触发注入的方法。(我认为那些整天在框架内编写代码的开发人员有时会忘记有些人实际上会实例化自己的对象。)


5
我不确定你是否理解 Guice 的设计原则。它明确旨在避免使用静态字段和状态,并且你应该尽可能避免直接引用注入器本身。你可以通过使用 @Inject 声明依赖关系来获取实例,而不是在注入器上调用 getInstance()。请注意,这里的翻译并未改变原意,只是将语言更加通俗易懂。 - Tim Gage
14
我唯一的观点(我说得相当啰嗦)是你需要从某个地方开始; 这是一个先有鸡还是先有蛋的问题。仅仅使用@Inject修饰方法是不够的,期望发生任何事情。必须在某个地方调用injector.getInstance()或injector.injectMembers()来启动它们。 - David Noha
1
在反射上下文中呢?在编译时你不知道想要的类是什么? - avanderw
3
“你不应该直接使用Injector”这句话的重点不是在“不应该直接使用”,而是“不应该注入 Injector”。 Guice在模块创建方面提供了很多检查,但如果模块内部最终注入了一个Injector,那么这些检查都无法起作用。 从Guice.createInjector(...myModules)手动创建的初始Injector可以完全正常地使用。 - Paul Féraud

8

正如 @gpampara 所说,Provider<T> 应该用于惰性/可选初始化。此外,就像我在回答你另一个问题时所说的那样,在几乎所有情况下,你应该避免在代码中引用 Injector

话虽如此,在 Guice 创建的类中,创建对象的 Injector 可以通过声明依赖关系注入。 在不声明任何绑定的情况下,Injector 就可以自动为注入提供服务。

如果您确实注入了 Injector,那么您应该思考一下为什么要这样做。 为什么不直接声明类所依赖的实际接口/类的依赖关系呢? 在构造函数中添加新依赖项与在代码其他地方检索某些依赖项的实例一样容易,并且使代码更加易于理解。


回复:“为什么你要那样做” - 例如,我想在引导之后即时创建动态代理。关于这个问题,我的提问在这里:http://stackoverflow.com/questions/34252268/best-practice-for-creating-dynamic-proxies-with-guice - Daniel Hári
另一个使用案例:我正在使用第三方库从存储/网络解组领域对象,并希望允许它们需要 DI 图中的服务。 - Guss
1
回复:“你为什么要这样做”。我正在使用Quartz,在作业中我想调用另一个服务-->我必须创建自己的JobFactory。为了实例化作业,我需要一个注入器。这就是关键点。 - Liem Le

3

可能不应该注入一个Injector实例的论点是相当合理的,但像任何规则一样,总有例外。

我有一个工厂类,它需要接受类引用以提供实例。这些实例不一定已知(实际上是已知的,但数量很多,可能还会有更多),因此我无法为它们创建所有的提供者。

public class ThingFactory {
    private Injector injector;

    @Inject
    ThingFactory(Injector injector) {
        this.injector = injector;
    }

    public <T> T getInstance(Class<T> aClass) {
        return injector.getInstance(aClass);
    }
}

我的应用程序中的真正类是扩展和覆盖另一个类,这就是为什么这个类基本上是Guice的传递。


2
一个例子是在使用Quartz时的JobFactory。谢谢。 - Liem Le

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,