带有 Guice 的现代类 DI
Modern Akka DI with Guice
Java8、这里是Guice 4.0和Akka 2.3.9。我正在尝试弄清楚如何使用 JSR330 样式 @Inject
注释来注释我的 actor 类,然后通过 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
的实例并正确地注入它们 with Java(不是斯卡拉!)?
请注意:FizzActor
不是我的演员系统中唯一的演员!
使用 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。
除非您尝试将 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);
}
}
编辑:
用@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");
}
}
使用 Creator 在 guice 模块的提供程序方法中创建 ActorRef
。要区分未类型化的不同 ActorRef
,请像使用任何 guice 系统一样在提供程序方法和注入点上使用注释。例如,
在您的 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);
}
}));
}
然后使用actor服务,注入特定的ActorRef
:
class ClientOfFizzActor {
@Inject
ClientOfFizzActor(@Named("fizzActor") ActorRef fizzActorRef) {..}
}
如果 Props.create(..)
子句是您的 actor class 中的静态工厂方法,它看起来会更干净。
如果有人发现这个问题,您需要使用 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);
}
}
所以我最近一直在玩Akka和Guice,感觉这两个玩的不太好。
我建议您采用与 Play 类似的方法。
Kutschkem 的回答与此最接近。
- 使用 ActorCreator 界面
- 确保你有一个无争论的
Creator
。不要尝试在 Creator
中执行 @AssisstedInject
,因为这意味着您需要为每个要创建的 Actor 创建一个新的创建者。我个人认为在 actor 中初始化它最好通过消息传递来完成。
- 让 ActorCreator 使用注入器,这样您就可以在 Creator 中轻松创建 Actor 对象。
这是使用当前 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);
}
}
不依赖于 Play 的通用 Akka Guice 集成,
请记住,不应该在演员系统中创建唯一的演员。
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();
}
}
为每个 Actor 创建 ActorRef 的通用 ActorRefProvider
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");
}
}
Java8、这里是Guice 4.0和Akka 2.3.9。我正在尝试弄清楚如何使用 JSR330 样式 @Inject
注释来注释我的 actor 类,然后通过 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
的实例并正确地注入它们 with Java(不是斯卡拉!)?
请注意:FizzActor
不是我的演员系统中唯一的演员!
使用 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。
除非您尝试将 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);
}
}
编辑:
用@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");
}
}
使用 Creator 在 guice 模块的提供程序方法中创建 ActorRef
。要区分未类型化的不同 ActorRef
,请像使用任何 guice 系统一样在提供程序方法和注入点上使用注释。例如,
在您的 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);
}
}));
}
然后使用actor服务,注入特定的ActorRef
:
class ClientOfFizzActor {
@Inject
ClientOfFizzActor(@Named("fizzActor") ActorRef fizzActorRef) {..}
}
如果 Props.create(..)
子句是您的 actor class 中的静态工厂方法,它看起来会更干净。
如果有人发现这个问题,您需要使用 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);
}
}
所以我最近一直在玩Akka和Guice,感觉这两个玩的不太好。
我建议您采用与 Play 类似的方法。
Kutschkem 的回答与此最接近。
- 使用 ActorCreator 界面
- 确保你有一个无争论的
Creator
。不要尝试在Creator
中执行@AssisstedInject
,因为这意味着您需要为每个要创建的 Actor 创建一个新的创建者。我个人认为在 actor 中初始化它最好通过消息传递来完成。 - 让 ActorCreator 使用注入器,这样您就可以在 Creator 中轻松创建 Actor 对象。
这是使用当前 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);
}
}
不依赖于 Play 的通用 Akka Guice 集成, 请记住,不应该在演员系统中创建唯一的演员。
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();
}
}
为每个 Actor 创建 ActorRef 的通用 ActorRefProvider
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");
}
}