我正在开发一个项目并尝试将两个不同的“数据服务”模块组合到一个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用于我的查询,但这似乎比我的工作要多得多如上所述)
答案 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。但是,你应该很少需要它。例如,可能需要创建仅在少量绑定中不同的对象树。这似乎不是你的情况 - 绑定注释是你的方法。