Guice commands
I recently checked in a new Guice extension called commands. The goal is simple - make it possible to modify your modules at runtime.But modules aren't naturally data, they're more like code:
Module module = new AbstractModule() {
protected void configure() {
bind(Soda.class).to(Coke.class);
bind(Snack.class).to(BuffaloRanchDoritos.class);
}
};
How can I convert this executable chunk of code into a manipulatable datastructure? Since Guice is built-on a well-defined API, this wasn't too difficult. I created a custom
Binder
that recorded the method calls into it made by the modules. I wrapped that in an API that converts the method calls into Command
objects: CommandRecorder commandRecorder = new CommandRecorder(new FutureInjector());
List<Command> commands = commandRecorder.recordCommands(module);
The
Command
interface has different implementation for each method* on Binder
. There's BindConstantCommand
, BindScopeCommand
, RequestStaticInjectionCommand
and several others. The interface supports visitors, so manipulating commands with type safety is easy. Finally, there's a class called
CommandReplayer
that can convert a list of commands back into a module. By extending the template methods, you can rewrite commands while you replay them: CommandReplayer rewriter = new CommandReplayer() {
@Override public <T> void replayBind(Binder binder, BindCommand<T> command) {
Key<? extends T> target = command.getTarget().getKey(null);
if (Key.get(Soda.class).equals(target)) {
binder.bind(Soda.class).to(Pepsi.class);
} else {
super.replayBind(binder, command);
}
}
};
Module rewrittenModule = rewriter.createModule(commands);
The last thing left to do is to build an injector from the rewritten module. It works!
Injector injector = Guice.createInjector(rewrittenModule);
assertEquals(new Pepsi(), injector.getInstance(Soda.class));
We're using commands in a soon-to-be-released test library. I suspect other Guice users will come up with more interesting uses of commands. If you're hacking on Guice, consider using commands when you need to tweak a module.