如何使用具有辅助参数的工厂的IndirectActorProducer?

时间:2016-12-08 13:53:57

标签: java playframework akka guice actor

我使用Akka和Guice依赖注入在我的Play Framework应用中使用IndirectActorProducer的模式。

像这样:

import akka.actor.Actor;
import akka.actor.IndirectActorProducer;
import play.api.inject.Injector;

public class GuiceActorProducer implements IndirectActorProducer {

    private Injector injector;
    private Class<Actor> cls;

    public GuiceActorProducer(Injector injector, Class<Actor> cls) {
        this.injector = injector;
        this.cls = cls;
    }

    @Override
    public Actor produce() {
        return injector.instanceOf(cls);
    }

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

这非常适合创建100%注入的actor,如下所示:

ActorRef imageGenerator = actorService.getActorSystem().actorOf(
                    Props.create(GuiceActorProducer.class, injector, ImageGenerator.class),
                    String.format("ImageGenerator-%d", brandId));

不幸的是,我有50%注入的演员和50%&#34;辅助&#34;。他们有这样的工厂:

public interface Factory {
    Synchronizer create(@Assisted("taskLogId") Long taskLogId, @Assisted("clientGroup") String clientGroup, @Assisted boolean messagesOnly, @Assisted("notes") String notes);
}

他们在模块中绑定了这样的内容:

bindActorFactory(Synchronizer.class, SynchronizerProtocol.Factory.class);

当我想创建一个Synchronizer actor时,我尝试这样但它不起作用。没有错误但它根本没有输入构造函数。这是正常的,我没有提供所需的参数......

ActorRef myActor = actorService.getActorSystem().actorOf(
                Props.create(GuiceActorProducer.class, injector, Synchronizer.class), synchronizerName);

我不知道在哪里使用这种语法提供我的4个参数。我非常确定我需要将它们送到注射器,但是我无法找到关于这个的任何文档,也没有找到api中指向这个方向的任何函数。

这是同步器的构造函数,只是为了清楚。它需要注入,因为它有很多工厂来创建其他objets。此外,Synchronizer actor需要在普通对象(而不是actor)中创建,并且需要命名为

@Inject
    private Synchronizer(SyncStateErrorUserNotification.Factory syncStateErrorUserNotificationFactory,
                         NewBrandStationLinkUserNotification.Factory newBrandStationLinkUserNotificationFactory,
                         MediaUpdaterProtocol.Factory mediaUpdatersFactory,
                         StationSyncFileGeneratorProtocol.Factory stationSyncFileGeneratorsFactory,
                         StationSynchronizerProtocol.Factory stationSynchronizerFactory,
                         BrandSyncFileGeneratorProtocol.Factory brandSyncFileGeneratorsFactory,
                         PlaylistUpdaterProtocol.Factory playlistUpdatersFactory,
                         Injector injector,
                         StationScheduleStyleService stationScheduleStyleService,
                         LogCreationService logCreationService,
                         CustomSqlManagerService customSqlManagerService,
                         SongSynchronizerProtocol.Factory songSynchronizerFactory,
                         ClientGroupSynchronizerProtocol.Factory clientGroupSynchronizersFactory) {
        super(logCreationService);
        this.syncStateErrorUserNotificationFactory = syncStateErrorUserNotificationFactory;
        this.newBrandStationLinkUserNotificationFactory = newBrandStationLinkUserNotificationFactory;
        this.mediaUpdatersFactory = mediaUpdatersFactory;
        this.playlistUpdatersFactory = playlistUpdatersFactory;
        this.injector = injector;
        this.stationScheduleStyleService = stationScheduleStyleService;
        this.logCreationService = logCreationService;
        this.customSqlManagerService = customSqlManagerService;
        this.stationSyncFileGeneratorsFactory = stationSyncFileGeneratorsFactory;
        this.brandSyncFileGeneratorsFactory = brandSyncFileGeneratorsFactory;
        this.stationSynchronizerFactory = stationSynchronizerFactory;
        this.songSynchronizerFactory = songSynchronizerFactory;
        this.clientGroupSynchronizersFactory = clientGroupSynchronizersFactory;
    }

帮助!感谢

1 个答案:

答案 0 :(得分:1)

当注入类的构造函数没有注入参数时,会发生

AssistedInject 。 Guice不能自己创建这样的实例。

工厂来帮助Guice创建实例:这个工厂是一个接口,它有一个方法(或几个方法)接收所有未注入的参数并返回一个适当的实例。

工厂界面安装在模块中,而不是绑定类。注入工厂而不是类实例;它用于创建类实例。您可以看到here更详细的说明和示例。

辅助注射的Akka方式是 IndirectActorProducer 。 Play在面纱下做了一些事情并建议了辅助注射的另一种模式,就像在documentation中解释的那样。

我个人发现在Play中使用辅助注入需要太多的样板代码,它应该作为最后的选择。我将展示两个选项:如何使用非注入参数注入actor(也就是通过辅助注入)以及如何使用Props创建它。

选项1:注入同步器

Synchronizer具有Factory接口,用于注入actor实例而不是Props:

SELECT *
FROM tbl
WHERE site Like "*" & [Forms]![NavigationForm]![NavigationSubform].[Form]![comboBox] & "*";

Play要求父级演员,这将提供辅助注入儿童演员。比方说,它是SynchronizerParent。父级在收到CreateSynchronizer消息时注入子节点,该消息封装了创建Synchronizer所需的所有参数。它将注入的子节点的ActorRef发送给调用者:

public class Synchronizer extends UntypedActor {
    private final Long taskLogId;
    private final String clientGroup;
    private final boolean messagesOnly;
    private final String notes;

    @Inject
    public Synchronizer(@Assisted Long taskLogId, @Assisted("clientGroup") String clientGroup, @Assisted boolean messagesOnly, @Assisted("notes") String notes) {
        this.taskLogId= taskLogId;
        this.clientGroup= clientGroup;
        this.messagesOnly= messagesOnly;
        this.notes= notes;
    }
    @Override
    public void onReceive(Object message) throws Exception {
        ...
    }
    // The factory
    public interface Factory {
        public Actor create(Long taskLogId, String clientGroup, boolean messagesOnly, String notes);
    }
}

模块中的绑定(应该实现AkkaGuiceSupport):

public class SynchronizerParent extends UntypedActor implements InjectedActorSupport {

    //Protocol
    public static class CreateSynchronizer {
        private final Long taskLogId;
        private final String clientGroup;
        private final boolean messagesOnly;
        private final String notes;
        private final String brandId;

        public CreateSynchronizer(
                Long taskLogId, String clientGroup, 
                boolean messagesOnly, String notes, String brandId) {
            this.taskLogId = taskLogId;
            this.clientGroup = clientGroup;
            this.messagesOnly = messagesOnly;
            this.notes = notes;
            this.brandId= brandId;
        }       
    }

    private Synchronizer.Factory childFactory;

    @Inject
    public SynchronizerParent(Synchronizer.Factory childFactory) {
        this.childFactory = childFactory;
    }

    @Override
    public void onReceive(Object message) throws Exception {
        if (message instanceof CreateSynchronizer) {
            injectSynchronizer((CreateSynchronizer)message);
        }
        else {
            unhandled(message);
        }       
    }

    private void injectSynchronizer(CreateSynchronizer injectMsg) {
        ActorRef child =  injectedChild(() -> childFactory.create(
            injectMsg.taskLogId, 
            injectMsg.clientGroup,
            injectMsg.messagesOnly,
            injectMsg.notes)
            , "child-" +injectMsg.brandId);
        sender().tell(child, self());
    }   
}

Actors服务使用 ask pattern :它将CreateSynchronizer消息发送给父actor,并期望将ActorRef接收回注入的子节点:

public class ActorsModule extends AbstractModule implements AkkaGuiceSupport {

    @Override
    protected void configure() {
        bindActor(SynchronizerParent.class, "parentActor");
        bindActorFactory(Synchronizer.class, Synchronizer.Factory.class);
    }
}

选项2:通过道具创建同步器

Syncronizer现在包含一个静态工厂方法props而不是Factory接口:

public class ActorsServiceImpl extends ActorsService {  
    private ActorRef parentActor;
    @Inject
    public ActorsServiceImpl(@Named("parentActor") ActorRef parentActor) {
        this.parentActor = parentActor;
    }

    public CompletionStage<ActorRef> createSyncronizer(Long taskLogId, String clientGroup, boolean messagesOnly, String notes, String brandId) {
        // Use guice assisted injection to instantiate and configure the child actor.
        long timeoutMillis = 100L;
        return FutureConverters.toJava(
            ask(parentActor,  
                new SynchronizerParent.CreateSynchronizer(taskLogId, clientGroup, messagesOnly, notes),
                brandId), 
            timeoutMillis))
        .thenApply(response -> (ActorRef) response);
    }
}

演员服务实施:

public class Synchronizer extends UntypedActor {
    private final Long taskLogId;
    private final String clientGroup;
    private final boolean messagesOnly;
    private final String notes;

    // Factory method
    public static Props props(Long taskLogId, String clientGroup, boolean messagesOnly, String notes) {
        return Props.create(Synchronizer.class, taskLogId, clientGroup, messagesOnly, notes);
    }

    public Synchronizer(Long taskLogId, String clientGroup, boolean messagesOnly, String notes) {
        this.taskLogId= taskLogId;
        this.clientGroup= clientGroup;
        this.messagesOnly= messagesOnly;
        this.notes= notes;
    }
    @Override
    public void onReceive(Object message) throws Exception {
        ...
    }
}

我绝对更喜欢使用第二种选择。