Overriding Binding in Guice

Overriding Binding in Guice

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(..):

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 in the Modules documentation.

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.

Override binding or child module in Guice

As the comments say, the full proper solution is a Scope. Assuming you've copied the implementation of SimpleScope from this article into your project, then here's what I'd do in your component. As you do above, I assume Foo and Bar are the objects you need created on a per-request basis, and X is the class of the thing you ultimately need Guice to create. I assume further that X has on it a method called doTheActualRequestLogic that is the method you wish to call to ultimately return back to the servlet:

public class MyWebComponent
{
private final Provider<X> theGuiceCreatedObject;
private final SimpleScope perRequestScope;

public MyWebComponent() {
perRequestScope = new SimpleScope();
Injector injector = Guice.createInjector(new AbstractModule()
{
@Override
protected void configure()
{
install(new NonChangingModule());
bind(Foo.class).toProvider(SimpleScope.seededKeyProvider())
.in(perRequestScope);
bind(Bar.class).toProvider(SimpleScope.seededKeyProvider())
.in(perRequestScope);
}
});
theGuiceCreatedObject = injector.getProvider(X.class);
}

// I assume methods called makeFoo and makeBar that can make
// a Foo or Bar for a request

// called by the web service to say "handle this request"
public RequestResult handleRequest(DataToMakeFooAndBarWith requestData) {
try {
perRequestScope.enter();
perRequestScope.seed(Foo.class, makeFoo(requestData));
perRequestScope.seed(Bar.class, makeBar(requestData));
return theGuiceCreatedObject.get().doTheActualRequestLogic(requestData);
} finally {
perRequestScope.exit();
}
}
}

Google guice, override configuration

I am not an expert on Play-specific setup, but generally this kind of design problem is solved in one of the following ways:

  1. No default. Remove the binding of DatabaseConnectionConfiguration from the upstream module (myapp-persistence), and define it in each downstream module (myapp-backend, myapp-rest) as appropriate.

  2. Default with override. Keep the default binding of DatabaseConnectionConfiguration like you did, implementing the most common configuration strategy there. Override it in downstream modules using Guice Modules.override(..) API when needed.

  3. Implement a unified configuration mechanism across the modules, that does not depend on particular frameworks used. (E.g. Bootique, which is built on Guice ... Haven't used it with Play though).

I personally prefer the approach #3, but in the absence of something like Bootique, #2 is a good substitute.

Guice override some bindings in an existing injector

The simplest way of thinking about this would be through child injectors, but that is explicitly disallowed as a design decision:

The reason overriding a binding in a child injector isn't supported is because it can lead a developer towards writing code that can work in either a parent & child injector, but have different behavior in each. This can lead to very surprising scenarios, because of just-in-time (JIT) bindings and the way they interact with parent/child injectors.

At this point, I would probably think about restructuring your application to avoid requiring these complicated bindings, but if you want to go further, you can probably use Injector.getBindings() or Injector.getAllBindings() (note the difference!) and stitch them back into a module using the Elements SPI. After all, Binding<?> extends Element, and Elements.getModule(...) will create a Module from your Elements. I haven't checked that it works, but that's probably your best lead.

Guice : How to customize the bindings of a third-party Module?

Here's how I would do it:

public class ThirdPartyModule extends AbstractModule {
@Override
protected void configure() {
// CoolWidget --
// \
// > WidgetInterface -> DefaultWidgetImpl
// /
// AwesomeWidget

OptionalBinder.newOptionalBinder(binder(), WidgetInterface.class)
.setDefault()
.to(DefaultWidgetImpl.class);

bind(CoolWidget.class).to(WidgetInterface.class);
bind(AwesomeWidget.class).to(WidgetInterface.class);
// etc.
}
}

public class MyAppModule extends AbstractModule {
@Override
protected void configure() {
OptionalBinder.newOptionalBinder(binder(), WidgetInterface.class)
.setBinding()
.to(CustomWidgetImpl.class);
}
}

By making all the bindings go indirectly through the WidgetInterface key, you only need to override that one binding.

Guice Modules.override on binding specified in multiple modules?

I figured out a solution, though it seems firmly in the hack category. I had to nest the overides.

Modules.override(Modules.override(childModule1).with(childModule2)).with(parent);

How do I subtract a binding using a Guice module override?

The SPI can do this. Use Elements.getElements(modA, modB) to get a list of Element objects, representing your bindings. Iterate through that list and remove the binding whose key you'd like to remove. Then create a module from the filtered elements using Elements.getModule(). Putting it all together:

public Module subtractBinding(Module module, Key<?> toSubtract) {
List<Element> elements = Elements.getElements(module);

for (Iterator<Element> i = elements.iterator(); i.hasNext(); ) {
Element element = i.next();
boolean remove = element.acceptVisitor(new DefaultElementVisitor<Boolean>() {
@Override public <T> Boolean visit(Binding<T> binding) {
return binding.getKey().equals(toSubtract);
}
@Override public Boolean visitOther(Element other) {
return false;
}
});
if (remove) {
i.remove();
}
}

return Elements.getModule(elements);
}


Related Topics



Leave a reply



Submit