Skip to main content

Binding transformations

In this tutorial, we will transform bindings declared in a separate standalone module. We will intercept a dependency exported in another module and transform it so that it adds some basic logging before method execution and after method finishes.

Example Launcher​

First, we will create a new Launcher by extending the Launcher class.

public class BindingTransformationExample extends Launcher {
...
}

Then let's define the Person interface with a single method void greet().

public interface Person {
void greet();
}

And inject an instance of a Person class into the application launcher by adding a person field annotated with the @Inject annotation.

@Inject
Person person;

Our application launcher overrides Launcher#run method just to call person.greet() method.

@Override
protected void run() {
person.greet();
}

Person module​

An application launcher would use 2 modules to inject a Person person dependency.

@Override
protected Module getModule() {
return Modules.combine(
new PersonModule(),
new PersonTransformModule()
);
}

The first one (PersonModule) is a simple AbstractModule that provides a single dependency for a Person class.

public static class PersonModule extends AbstractModule {
@Provides
Person greeter() {
return () -> System.out.println("Hello!");
}
}

A provided Person simply prints "Hello!" when greet() method is called.

Person transformation module​

The PersonTransformModule is a module that intercepts the provided Person class and adds logging to the greet() method calls.

public static class PersonTransformModule extends AbstractModule {
@Override
protected void configure() {
transform(Person.class, (bindings, scope, key, binding) ->
binding.mapInstance(person ->
() -> {
System.out.println("Start of greeting");
person.greet();
System.out.println("End of greeting");
}
));
}
}

The module does not export any dependency using the @Provides annotation. Instead it overrides AbstractModule#configure method only to call the transform(...) method. Let's take a closer look at the transform(...) call. It takes 2 arguments:

  • Class - the class of a binding to be transformed (it can be changed to a KeyPattern to transform all bindings matched by a key pattern)
  • BindingTransformer<T> - a transformer which is itself a functional interfaces that accepts bindings, scope, current key and binding and returns a new binding (or the same binding if no transformation occurred)

We call Binding#mapInstance method on the received binding and specify how the Person instance should be transformed. Here we return a new instance of the Person class, which adds some System.out logs before and after calling person.greet() on an original person instance.

Launching the example​

To launch the example, we need to define the main method that simply creates a new instance of our application launcher and passes the application arguments to the Launcher#launch method.

public static void main(String[] args) throws Exception {
BindingTransformationExample launcher = new BindingTransformationExample();
launcher.launch(args);
}

When launching the application it should print to stdout:

Start of greeting
Hello!
End of greeting

You can find example sources on Github