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 executing the method and after the method finishes.

Example Launcher

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

public class BindingTransformationExample extends Launcher {    ...}

Then let's define a 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 @Inject annotation.

@InjectPerson person;

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

@Overrideprotected void run() {  person.greet();}

Person module

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

@Overrideprotected 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

A PersonTransformModule is a module that would intercept a provided Person class and add logging to 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 @Provides annotation. Instead it overrides AbstractModule#configure method just to call transform(...) method. Let's take a closer look at transform(...) call. It takes 2 arguments:

  • Class - a class of a binding which will be transformed (this 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 takes bindings, scope, current key and binding and returns a new binding (or the same binding if no transformation happened)

We call Binding#mapInstance method on a a received binding and specify how an instance of a Person should be transformed. Here, we return a new instance of Person class that adds some System.out logging 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 which would simply create a new instance of our application launcher and pass application arguments to 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 greetingHello!End of greeting

You can find example sources on Github