Java 在 Guice 中覆盖绑定
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/483087/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Overriding Binding in Guice
提问by tddmonkey
I've just started playing with Guice, and a use-case I can think of is that in a test I just want to override a single binding. I think I'd like to use the rest of the production level bindings to ensure everything is setup correctly and to avoid duplication.
我刚刚开始使用 Guice,我能想到的一个用例是在测试中我只想覆盖单个绑定。我想我想使用其余的生产级别绑定来确保一切设置正确并避免重复。
So imagine I have the following Module
所以想象我有以下模块
public class ProductionModule implements Module {
public void configure(Binder binder) {
binder.bind(InterfaceA.class).to(ConcreteA.class);
binder.bind(InterfaceB.class).to(ConcreteB.class);
binder.bind(InterfaceC.class).to(ConcreteC.class);
}
}
And in my test I only want to override InterfaceC, while keeping InterfaceA and InterfaceB in tact, so I'd want something like:
在我的测试中,我只想覆盖 InterfaceC,同时保持 InterfaceA 和 InterfaceB 机智,所以我想要类似的东西:
Module testModule = new Module() {
public void configure(Binder binder) {
binder.bind(InterfaceC.class).to(MockC.class);
}
};
Guice.createInjector(new ProductionModule(), testModule);
I've also tried the following, with no luck:
我也试过以下,但没有运气:
Module testModule = new ProductionModule() {
public void configure(Binder binder) {
super.configure(binder);
binder.bind(InterfaceC.class).to(MockC.class);
}
};
Guice.createInjector(testModule);
Does anyone know if it's possible to do what I want or am I completely barking up the wrong tree??
有谁知道是否可以做我想做的事,还是我完全在错误的树上吠叫?
--- Follow up: It would seem I can achieve what I want if I make use of the @ImplementedBy tag on the interface and then just provide a binding in the test case, which works nicely when there is a 1-1 mapping between the interface and implementation.
--- 跟进:如果我在接口上使用@ImplementedBy 标签,然后在测试用例中提供一个绑定,似乎我可以实现我想要的,当它们之间存在 1-1 映射时,它可以很好地工作接口和实现。
Also, after discussing this with a colleague it would seem we'd head down the road of overriding an entire module and ensuring we have our modules defined correctly. This seems like it might cause a problem though where a binding is misplaced in a module and needs to be moved, thus possibly breaking a load of tests as bindings may no longer be available to be overriden.
此外,在与同事讨论这个问题后,我们似乎会走上覆盖整个模块并确保我们正确定义模块的道路。这似乎可能会导致问题,尽管绑定在模块中放错了位置并且需要移动,因此可能会破坏测试负载,因为绑定可能不再可以被覆盖。
采纳答案by albertb
This might not be the answer you're looking for, but if you're writing unit tests, you probably shouldn't be using an injector and rather be injecting mock or fake objects by hand.
这可能不是您正在寻找的答案,但如果您正在编写单元测试,您可能不应该使用注入器,而应该手动注入模拟或假对象。
On the other hand, if you really want to replace a single binding, you could use Modules.override(..)
:
另一方面,如果你真的想替换单个绑定,你可以使用Modules.override(..)
:
public class ProductionModule implements Module {
public void configure(Binder binder) {
binder.bind(InterfaceA.class).to(ConcreteA.class);
binder.bind(InterfaceB.class).to(ConcreteB.class);
binder.bind(InterfaceC.class).to(ConcreteC.class);
}
}
public class TestModule implements Module {
public void configure(Binder binder) {
binder.bind(InterfaceC.class).to(MockC.class);
}
}
Guice.createInjector(Modules.override(new ProductionModule()).with(new TestModule()));
See details here.
在此处查看详细信息。
But as the javadoc for Modules.overrides(..)
recommends, you should design your modules in such a way that you don't need to override bindings. In the example you gave, you could accomplish that by moving the binding of InterfaceC
to a separate module.
但是正如 javadoc forModules.overrides(..)
建议的那样,您应该以不需要覆盖绑定的方式设计模块。在您提供的示例中,您可以通过将 的绑定移动InterfaceC
到单独的模块来实现。
回答by Mon Calamari
Why not to use inheritance? You can override your specific bindings in overrideMe
method, leaving shared implementations in configure
method.
为什么不使用继承?您可以在overrideMe
方法中覆盖您的特定绑定,而在方法中保留共享实现configure
。
public class DevModule implements Module {
public void configure(Binder binder) {
binder.bind(InterfaceA.class).to(TestDevImplA.class);
overrideMe(binder);
}
protected void overrideMe(Binder binder){
binder.bind(InterfaceC.class).to(ConcreteC.class);
}
};
public class TestModule extends DevModule {
@Override
public void overrideMe(Binder binder) {
binder.bind(InterfaceC.class).to(MockC.class);
}
}
And finally create your injector this way:
最后以这种方式创建您的注射器:
Guice.createInjector(new TestModule());
回答by esukram
You want to use Juckitowhere you can declare your custom configuration for each test class.
您想使用Juckito,您可以在其中为每个测试类声明自定义配置。
@RunWith(JukitoRunner.class)
class LogicTest {
public static class Module extends JukitoModule {
@Override
protected void configureTest() {
bind(InterfaceC.class).to(MockC.class);
}
}
@Inject
private InterfaceC logic;
@Test
public testLogicUsingMock() {
logic.foo();
}
}
回答by Jan Gassen
If you don't want to change your production module and if you have a default maven-like project structure like
如果您不想更改生产模块,并且您有一个类似 Maven 的默认项目结构,例如
src/test/java/...
src/main/java/...
You can just create a new class ConcreteC
in your test directory using the same package as for your original class. Guice will then bind InterfaceC
to ConcreteC
from your test directory whereas all other interfaces will be bound to your production classes.
您可以ConcreteC
使用与原始类相同的包在测试目录中创建一个新类。Guice 然后将从您的测试目录绑定InterfaceC
到ConcreteC
,而所有其他接口将绑定到您的生产类。
回答by Dave T.
In a different setup, we have more than one activity defined in separate modules. The activity that's being injected into is in an Android Library Module, with its own RoboGuice module definition in the AndroidManifest.xml file.
在另一种设置中,我们在不同的模块中定义了多个活动。被注入的活动在一个 Android 库模块中,在 AndroidManifest.xml 文件中有它自己的 RoboGuice 模块定义。
The setup looks like this. In the Library Module there are these definitions:
设置看起来像这样。在库模块中有这些定义:
AndroidManifest.xml:
AndroidManifest.xml:
<application android:allowBackup="true">
<activity android:name="com.example.SomeActivity/>
<meta-data
android:name="roboguice.modules"
android:value="com.example.MainModule" />
</application>
Then we have a type being injected:
然后我们有一个类型被注入:
interface Foo { }
Some default implementation of Foo:
Foo 的一些默认实现:
class FooThing implements Foo { }
MainModule configures the FooThing implementation for Foo:
MainModule 为 Foo 配置 FooThing 实现:
public class MainModule extends AbstractModule {
@Override
protected void configure() {
bind(Foo.class).to(FooThing.class);
}
}
And finally, an Activity that consumes Foo:
最后,一个消耗 Foo 的 Activity:
public class SomeActivity extends RoboActivity {
@Inject
private Foo foo;
}
In the consuming Android Application Module, we would like to use SomeActivity
but, for testing purposes, inject our own Foo
.
在消费 Android 应用程序模块中,我们想使用SomeActivity
但为了测试目的,注入我们自己的Foo
.
public class SomeOtherActivity extends Activity {
@Override
protected void onResume() {
super.onResume();
Intent intent = new Intent(this, SomeActivity.class);
startActivity(intent);
}
}
One might argue to expose the module handling to the client application, however, we need to mostly hide the components being injected because the Library Module is an SDK, and exposing pieces has larger implications.
有人可能会争辩说将模块处理公开给客户端应用程序,但是,我们需要主要隐藏被注入的组件,因为库模块是一个 SDK,并且公开部分具有更大的影响。
(Remember, this is for testing, so we know the internals of SomeActivity, and know it consumes a (package visible) Foo).
(记住,这是为了测试,所以我们知道 SomeActivity 的内部结构,并且知道它消耗(包可见)Foo)。
The way I found that works makes sense; use the the suggested override for testing:
我发现有效的方式是有道理的;使用建议的覆盖进行测试:
public class SomeOtherActivity extends Activity {
private class OverrideModule
extends AbstractModule {
@Override
protected void configure() {
bind(Foo.class).to(OtherFooThing.class);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RoboGuice.overrideApplicationInjector(
getApplication(),
RoboGuice.newDefaultRoboModule(getApplication()),
Modules
.override(new MainModule())
.with(new OverrideModule()));
}
@Override
protected void onResume() {
super.onResume();
Intent intent = new Intent(this, SomeActivity.class);
startActivity(intent);
}
}
Now, when SomeActivity
is started, it will get OtherFooThing
for its injected Foo
instance.
现在,当SomeActivity
启动时,它将获取OtherFooThing
其注入的Foo
实例。
It's a very specific situation where, in our case, OtherFooThing was used internally to record test situations, while FooThing was used, by default, for all other uses.
这是一种非常特殊的情况,在我们的例子中,OtherFooThing 在内部用于记录测试情况,而 FooThing 默认用于所有其他用途。
Keep in mind, we areusing #newDefaultRoboModule
in our unit tests, and it works flawlessly.
请记住,我们在单元测试中使用#newDefaultRoboModule
它,它运行完美。