java - usando la inyección de guice con el actor lanza un puntero nulo
dependency-injection akka (3)
Obtengo una excepción de puntero nulo en la inyección de campo de un servidor que se inició como un actor Akka.
Parte schedular:
private ActorRef myActor = Akka.system().actorOf(
new Props(Retreiver.class));
@Override
public void onStart(Application app) {
log.info("Starting schedular.....!");
Akka.system()
.scheduler()
.schedule(Duration.create(0, TimeUnit.MILLISECONDS),
Duration.create(30, TimeUnit.MINUTES), myActor, "tick",
Akka.system().dispatcher());
}
Parte de la clase Retreiver:
public class Retreiver extends UntypedActor {
private Logger.ALogger log = Logger.of(Retreiver .class);
@Inject
private myDataService dataService;
@Override
public void onReceive(Object arg0) throws Exception {
if (0 != dataService.getDataCount()) {
....
....
....
}
}
Me estoy poniendo nulo para dataService. Por favor, avísenme sobre esto.
Gracias.
NullPointerException
la NullPointerException
porque Akka está creando instancias de tu actor Retriever
y no de Guice. Es necesario que Guice construya su instancia y luego se la pase a Akka. IndirectActorProducer
puede ayudarlo a lograr esto, por ejemplo:
class RetrieverDependencyInjector implements IndirectActorProducer {
final Injector injector;
public RetrieverDependencyInjector(Injector injector) {
this.injector = injector;
}
@Override
public Class<? extends Actor> actorClass() {
return Retriever.class;
}
@Override
public Retriever produce() {
return injector.getInstance(Retriever.class);
}
}
Tenga en cuenta que produce()
debe crear una nueva instancia Actor
cada vez que se invoca, no puede devolver la misma instancia.
A continuación, puede obtener Akka para recuperar su actor a través de RetrieverDependencyInjector
, por ejemplo:
ActorRef myActor = Akka.system().actorOf(
Props.create(RetrieverDependencyInjector.class, injector)
);
ACTUALIZAR
Pensé en ti comentar más, podrías convertir RetrieverDependencyInjector
en un GenericDependencyInjector
al proporcionar la clase del Actor
que deseas como parámetro de constructor, que quizás te permita hacer algo como:
Props.create(GenericDependencyInjector.class, injector, Retriever.class)
No he intentado esto, pero podría darte un punto de partida.
Para cualquiera que necesite esto:
public class GuiceInjectedActor implements IndirectActorProducer {
final Injector injector;
final Class<? extends Actor> actorClass;
public GuiceInjectedActor(Injector injector, Class<? extends Actor> actorClass) {
this.injector = injector;
this.actorClass = actorClass;
}
@Override
public Class<? extends Actor> actorClass() {
return actorClass;
}
@Override
public Actor produce() {
return injector.getInstance(actorClass);
}
}
Y
Akka.system().actorOf(Props.create(GuiceInjectedActor.class, INJECTOR,Retreiver.class))
Eso es...!!!
Puede haber otras formas, por ejemplo, puede colocar un controlador de inyector estático en Boot o Some-Injector-Holder-class, e inyectar al crear el actor mediante el método heredado: preStart ()
public class Retreiver extends UntypedActor {
private Logger.ALogger log = Logger.of(Retreiver .class);
@Override
public void preStart () {
super.preStart ();
Boot.injector.injectMembers (this);
//Some-Injector-holder.injectMembers (this);
}
@Inject
private myDataService dataService;
@Override
public void onReceive(Object arg0) throws Exception {
if (0 != dataService.getDataCount()) {
....
....
....
}
}
y también, también puede inyectar el inyector en el proveedor de actor para inicializar al actor por el inyector en un alcance de UntypedActorFactory:
public class InjectingActorProvider implements Provider<ActorRef> {
@Inject
private Injector injector;
@SuppressWarnings("serial")
@Override
public final ActorRef get() {
Props props = new Props(new UntypedActorFactory() {
@Override
public Actor create() {
return injector.getInstance(actorClass);
}
});
props = props.withRouter(...);
props = props.withDispatcher(...);
actor = system.actorOf(props, actorName);
return actor;
}
}