Search code examples
javaakkaguice

Modern Akka DI with Guice


Java 8, Guice 4.0 and Akka 2.3.9 here. I am trying to figure out how to annotate my actor classes with JSR330-style @Inject annotations, and then wire them all up via Guice.

But literally every single article I have read (some examples below) either uses Scala code examples, a criminally-old version of Guice, or a criminally-old version of Akka:

So, given the following Guice module:

public interface MyService {
    void doSomething();
}

public class MyServiceImpl implements MyService {
    @Override
    public void doSomething() {
        System.out.println("Something has been done!");
    }
}

public class MyActorSystemModule extends AbstractModule {
    @Override
    public void configure() {
        bind(MyService.class).to(MyServiceImpl.class);
    }
}

And given the FizzActor that gets injected with a MyService:

public class FizzActor extends UntypedActor {
    private final MyService myService;

    @Inject
    public FizzActor(MyService myService) {
        super();

        this.myService = myService;
    }

    @Override
    public void onReceive(Object message) {
        // .. Do fizz stuff inside here.
    }
}

Then I ask: How do I rig up MyActorSystemModule to create instances of FizzActor and properly inject them with Java (not Scala!)?

Please note: FizzActor is not the only actor in my actor system!


Solution

  • Use Creator to create ActorRefs in provider methods of your guice module. To distinguish between the different ActorRefs, which are untyped, use annotations on your provider methods and injection points as you would any guice system. For example,

    In your guice module:

    @Override
    protected void configure() {
        bind(ActorSystem.class).toInstance(ActorSystem.apply());
        bind(FizzService.class).toInstance(new FizzServiceImpl());
    }
    
    @Provides @Singleton @Named("fizzActor")
    ActorRef serviceActorRef(final ActorSystem system, final FizzService fizzService) {
        return system.actorOf(Props.create(new Creator<Actor>() {
            @Override
            public Actor create() throws Exception {
                return new FizzActor(fizzService);
            }
        }));
    }
    

    Then to use the actor service, inject a specific ActorRef:

    class ClientOfFizzActor {
        @Inject
        ClientOfFizzActor(@Named("fizzActor") ActorRef fizzActorRef) {..}
    }
    

    It looks cleaner if the Props.create(..) clause is a static factory method in your actor class.