Современный Акка Д.И. с Guice

Java 8, Guice 4.0 и Akka 2.3.9. Я пытаюсь выяснить, как аннотировать классы моего актера с помощью аннотаций @Inject в стиле JSR330, а затем подключить их все через Guice.

Но буквально каждая статья, которую я прочитал (некоторые примеры ниже), либо использует примеры кода Scala, либо уголовно-старую версию Guice, либо уголовно-старую версию Akka:

Итак, учитывая следующий модуль Guice:

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);
    }
}

И учитывая FizzActor, который вводится с помощью 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.
    }
}

Затем я спрашиваю: Как настроить MyActorSystemModule для создания экземпляров FizzActor и правильно вставлять их с помощью Java (не Scala!)?

Обратите внимание: FizzActor - не единственный актер в моей системе актеров!

Ответ 1

Используйте Creator для создания ActorRef в методах поставщика вашего модуля guice. Чтобы различать различные ActorRef s, которые являются нетипизированными, используйте аннотации для методов вашего провайдера и точек впрыска, как и любая система guice. Например,

В вашем графическом модуле:

@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);
        }
    }));
}

Затем, чтобы использовать услугу актера, введите конкретный ActorRef:

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

Он выглядит более чистым, если предложение Props.create(..) представляет собой статический метод factory в вашем классе актера.

Ответ 2

Если вы не пытаетесь привязать UntypedActor к FizzActor, вы можете просто ввести его в другие классы, как есть:

class SomeOtherClass {

    @Inject 
    public SomeOtherClass(FizzActor fizzActor) {
        //do stuff
    }
}

Если вы пытаетесь связать его с интерфейсом, вам нужно специально сделать это в модуле:

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

Edit:

Как использовать @Named для выделения UntypedActor, например:

class SomeOtherClass {

    @Inject 
    public SomeOtherClass(@Named("fizzActor")UntypedActor fizzActor, @Named("fooActor") UntypedActor fooActor) {
        //do stuff
    }
}

Затем в вашем модуле вы можете выполнить поиск akka:

public class MyActorSystemModule extends AbstractModule {

    ActorSystem system = ActorSystem.create("MySystem");

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

    @Provides
    @Named("fizzActor")
    public UntypedActor getFizzActor() {
        return system.actorOf(Props.create(FizzActor.class), "fizzActor");
    }

    @Provides
    @Named("fooActor")
    public UntypedActor getFooActor() {
        return system.actorOf(Props.create(FooActor.class), "fooActor");
    }
}

Ответ 3

Используйте akka Creator:

public class GuiceCreator<T> implements Creator<T> {
 Class<T> clz;
 Module module;
 /*Constructor*/

 public T create() {
    Injector injector = Guice.createInjector(this.module);
    return injector.getInstance(this.clz);
  }
}

Затем используйте Props.create с вашим блестящим новым создателем на основе guice.

Отказ от ответственности: я фактически не знаю Akka, упомянутая информация поступает из просмотра документации и JavaDoc.

Ответ 4

В случае, если кто-либо нашел этот вопрос, вам нужно использовать IndirectActorProducer, я упомянул пример Spring и изменил его, чтобы вместо этого использовать Guice,

/**
 * An actor producer that lets Guice create the Actor instances.
 */
public class GuiceActorProducer implements IndirectActorProducer {
    final String actorBeanName;
    final Injector injector;
    final Class<? extends Actor> actorClass;

    public GuiceActorProducer(Injector injector, String actorBeanName, Class<? extends Actor> actorClass) {
        this.actorBeanName = actorBeanName;
        this.injector = injector;
        this.actorClass = actorClass;
    }

    @Override
    public Actor produce() {
        return injector.getInstance(Key.get(Actor.class, Names.named(actorBeanName)));
    }

    @Override
    public Class<? extends Actor> actorClass() {
        return actorClass;
    }
}

В модуле

public class BookingModule extends AbstractModule {

    @Override
    protected void configure() {               
        // Raw actor class, meant to be used by GuiceActorProducer.
        // Do not use this directly
        bind(Actor.class).annotatedWith(
                Names.named(BookingActor.ACTOR_BEAN_NAME)).to(
                BookingActor.class);
    }

    @Singleton
    @Provides
    @Named(BookingActor.ACTOR_ROUTER_BEAN_NAME)
    ActorRef systemActorRouter(Injector injector, ActorSystem actorSystem) {
      Props props = Props.create(GuiceActorProducer.class, injector, BookingActor.ACTOR_BEAN_NAME, actorClass);
      actorSystem.actorOf(props.withRouter(new RoundRobinPool(DEFAULT_ROUTER_SIZE)), BookingActor.ACTOR_ROUTER_BEAN_NAME);
    }
}

Ответ 5

Итак, я недавно много играл с Аккой и Гисом, и я чувствую, что эти двое не слишком хорошо играют вместе.

То, что я предлагаю, - это подход, аналогичный тому, что играет.

Ответ Кутчкема ближе всего к этому.

  • используйте интерфейс ActorCreator
  • убедитесь, что у вас нет аргументов Creator. Не пытайтесь сделать @AssisstedInject в Creator, поскольку это будет означать, что вам понадобится новый создатель для каждого Актера, которого вы хотите создать. Лично я считаю, что инициализация этого в актере лучше делать с помощью обмена сообщениями.
  • пусть ActorCreator потребляет инжектор, чтобы вы могли легко создать объект Актера внутри Создателя.

Вот пример кода, использующий текущий Akka 2.5. Это предпочтительная настройка, которую мы выбрали для нашего развертывания Akka 2.5. Для краткости я не предоставлял модуль, но он должен быть ясным из того, как вводятся члены, что вы хотите предоставить.

код:

 class ActorCreator implements Creator<MyActor>
   @Inject
   Injector injector;
   public MyActor create() {
     return injector.getInstance(MyActor.class);
   }
 }

 class MyActor extends AbstractActor {
   @Inject
   SomeController object;

   @Nullable
   MyDataObject data;

   public ReceiveBuilder createReceiveBuilder() {
    return receiveBuilder()
      .match(MyDataObject.class, m -> { /* doInitialize() */ })
      .build(); 
   }
}

class MyParentActor extends AbstractActor {
   @Inject
   ActorCreator creator;

   void createChild() {
     getContext().actorOf(new Props(creator));
   }

   void initializeChild(ActorRef child, MyDataObject obj) {
     child.tell(obj);
   }
}

Ответ 6

Универсальная интеграция Akka Guice без зависимости от Play, помните, что в системе акторов должен быть создан не единственный актер.

import akka.actor.Actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import com.google.inject.AbstractModule;
import com.google.inject.Provider;
import com.google.inject.name.Names;

public abstract class AkkaGuiceModule extends AbstractModule {

    protected <T extends Actor> void bindActor(Class<T> actorClass, String name) {
        bind(actorClass);

        Provider<ActorSystem> actorSystemProvider = getProvider(ActorSystem.class);
        Provider<T> actorProvider = getProvider(actorClass);

        bind(ActorRef.class)
                .annotatedWith(Names.named(name))
                .toProvider(ActorRefProvider.of(actorSystemProvider, actorProvider, name))
                .asEagerSingleton();
    }

}

Общий ActorRefProvider для создания ActorRef для каждого актера

import akka.actor.Actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.japi.Creator;
import com.google.inject.Provider;
import lombok.Value;

@Value(staticConstructor = "of")
public class ActorRefProvider<T extends Actor> implements Provider<ActorRef> {

    private Provider<ActorSystem> actorSystemProvider;
    private Provider<T> actorProvider;
    private String name;

    public final class ActorCreator implements Creator<Actor> {
        @Override
        public Actor create() {
            return actorProvider.get();
        }
    }

    @Override
    public ActorRef get() {
        return actorSystemProvider.get().actorOf(Props.create(new ActorCreator()), name);
    }

}

Пример использования

import akka.actor.ActorSystem;
import com.google.inject.Provides;
import com.typesafe.config.Config; // optional

public class MyAkkaModule extends AkkaGuiceModule {

    @Provides
    @Singleton
    ActorSystem actorSystem(Config config) {
        return ActorSystem.create("actor-system-name", config);
    }

    @Override
    protected void configure() {
        bindActor(SomeActor1.class, "actorName1");
        bindActor(SomeActor2.class, "actorName2");
    }

}