如何将 IndirectActorProducer 与具有辅助参数的工厂一起使用?

How to use IndirectActorProducer with a factory having Assisted params?

我在使用 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% 注入的演员 "assisted"。他们有一个这样的工厂:

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 中的任何函数,这些都指向我这个方向。

这是 Synchronizer 的构造函数,只是为了清楚起见。它需要被注入,因为它有很多工厂来创建其他对象。另外,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;
    }

求助!谢谢

AssistedInject 在注入 class 的构造函数具有 not-injected 参数时发生。 Guice 不能自己创建这样的实例。

一个工厂来协助Guice创建实例:这个工厂是一个接口,它有一个方法(或几个方法)接收所有not-injected参数和returns一个合适的实例。

工厂接口安装在模块中而不是绑定class。注入工厂而不是 class 实例;它用于创建 class 实例。您可以查看here更详细的解释和示例。

Akka 的辅助注入方式是IndirectActorProducer。 Play 隐藏了一些东西,并提出了另一种辅助注射模式,就像 documentation 中解释的那样。

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

选项 1:注入 Synchronizer

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

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

播放需要 parent 演员,这将提供 child 演员的辅助注入。比方说,它是 SynchronizerParent。 parent 在接收到 CreateSynchronizer 消息时注入 child,该消息封装了创建同步器所需的所有参数。它将注入的 child 的 ActorRef 发送给调用者:

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

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

public class ActorsModule extends AbstractModule implements AkkaGuiceSupport {

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

Actors 服务使用 ask 模式:它向 parent actor 发送 CreateSynchronizer 消息,并期望将 ActorRef 接收回注入的 child:

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

选项 2:通过 Props 创建同步器

Syncronizer 现在包含静态工厂方法道具而不是工厂接口:

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 {
        ...
    }
}

Actor 服务实现:

public class ActorsServiceImpl extends ActorsService {
    ...
    public ActorRef createSynchronizer(Long taskLogId, String clientGroup, 
        boolean messagesOnly, String notes, String brandId) {
    return getActorSystem().actorOf(Synchronizer.props(
            taskLogId, clientGroup, messagesOnly, notes), 
            "ImageGenerator-" + brandId);       
    }
}

我绝对更喜欢使用第二个选项。