Guice:如何为一个类型获得多个@Provides?

时间:2014-01-11 01:13:11

标签: java hibernate guice

我正在开发一个项目并尝试将两个不同的“数据服务”模块组合到一个Web应用程序中(目前,该应用程序是一个桌面Swing应用程序)。

每个模块都有自己的Guice(私有)模块。每个Guice模块都包含:

@Provides
@Inject
protected JPQLQuery provideJPQLQuery(EntityManager entityManager) {
    return new JPAQuery(entityManager);
}

稍后将在从db:

查找内容的类的构造函数中使用它
@Inject
public SomeClassThatLooksObjectsUpFromDatabase(Provider<JPQLQuery> queryProvider) {
    this.queryProvider = queryProvider;
}

然后'queryProvider'可以执行查询。

现在,当只安装了一个Guice模块时,这样可以正常工作,但是一旦安装了这两个模块,我(可预见地)会收到此错误:

Unable to create binding for com.mysema.query.jpa.JPQLQuery.  It was already configured on one or more child injectors or private modules
bound at ServiceOneGuiceModule.provideJPQLQuery()
bound at ServiceTwoGuiceModule.provideJPQLQuery()

现在,我明白为什么会破坏 - 我说有两个类型为JPQLQuery的提供者,Guice不知道使用哪一个。

有什么方法可以让Guice将这些提供商分开?我想这样做是因为每个模块都有自己正确配置的Hibernate实体,每个实体都有自己独特的数据源(这个项目中有多个数据库)。

理想情况下,它会涉及以某种方式命名这些提供程序并按名称注入它们(例如我可以单独注入“ServiceOneJPQLQueryProvider”和“ServiceTwoJPQLQueryProvider”),但我还没有找到任何方法来实现这样的事情。< / p>

(我想一个替代方案是以某种方式配置Hibernate,因此它拥有它需要的所有不同的数据源,然后我可能只需要一个Provider用于我的查询,但这似乎比我的工作要多得多如上所述)

2 个答案:

答案 0 :(得分:5)

看看binding annotations,他们习惯于解决你遇到的问题。

建议使用 @Named ,因为它们是类型安全的,并且如果拼错它们将产生编译错误而不是运行时错误。

简而言之:

ServiceOne.java:

@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
public @interface ServiceOne {}

ServiceTwo.java:

@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
public @interface ServiceTwo {}

ServiceOneModule.java:

@Provides
@ServiceOne
@Inject
protected JPQLQuery provideJPQLQuery(EntityManager entityManager) {
    return new JPAQuery(entityManager);
}

SomeClass.java:

@Inject
public SomeClassThatLooksObjectsUpFromDatabase(@ServiceOne Provider<JPQLQuery> queryProvider) {
    this.queryProvider = queryProvider;
}

答案 1 :(得分:0)

首先,你对@Named不正确,因为提供者在Guice中无处不在 - 事实上,当你创建像bind(...).to(...)这样的绑定或任何其他非提供者绑定时,你隐式创建提供者。当你直接注入对象时,它只是注入提供者的捷径 - 提供者注入和直接注入在Guice中是可互换的(当你不使用像RequestScoped这样的特殊范围时,这更像是这个快捷方式的实现细节) 。例如,即使您为@Provides创建了JPQLQuery方法,您仍然可以直接注入它,而无需提供者:

@Inject
public SomeClass(JPQLQuery query) {
    ...
}

使用哪种方法取决于您的要求(您在JPQLQuery方法中需要几个 SomeClass实例?)和范围正在使用中(例如,当{的范围时{1}}比SomeClass更广泛 - 例如,当JPQLQuery是单身,SomeClass是请求范围时 - 有时提供者使用是强制性的。)

由于您使用JPQLQuery创建了@Named绑定,但仍然没有注释注入@Named,因此您无法通过@Provides获得所需内容。它不起作用,因为绑定键包含注入类型和(可能不存在)绑定注释,因此如果您只有指定注释的绑定但注入提供者或没有注释的类型,Guice将失败。您还应该将Provider<...>注释放在构造函数参数上:

@Named

这种方式应该可行。

顺便说一下,我强烈建议不要@Inject public SomeClass(@Named("ServiceOne") Provider<JPQLQuery> queryProvider) { ... } 。您最好为同一个类的每个不同绑定创建单独的绑定注释,如here所述。使用自定义注释时,注入时无法在绑定名称时出错。

还有更先进的机器来操纵类似的绑定。它被称为private modules。但是,你应该很少需要它。例如,可能需要创建仅在少量绑定中不同的对象树。这似乎不是你的情况 - 绑定注释是你的方法。