如何将 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);
}
}
我绝对更喜欢使用第二个选项。
我在使用 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);
}
}
我绝对更喜欢使用第二个选项。