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