玩2.4 - 如何使用guice注入akka actor

如何在Play 2.4中创建一个Actor,以便它使用Guice自动注入依赖项?我读了几篇文章,他们使用了GuiceApplicationBuilder,然后创建了actor . 我不想控制实例化播放应用程序的方式 . 相反,我只想知道如何创建一个actor实例,以便使用Guice管理依赖项 .

创建一个包含5个worker的RoundRobinRouter

class RouterActor extends Actor with ActorLogging {
  val router = {
    val routees = Vector.fill(5) {
      val r = context.actorOf(Props[Worker])
      context watch r
      ActorRefRoutee(r)
    }
    Router(RoundRobinRoutingLogic(), routees)
  }

  def receive = {
    case job: Job =>
    router.route(job, sender())
  }
}

扩展AkkaGuiceSupport并创建绑定RouterActor的模块

class RouterActorModule extends AbstractModule with AkkaGuiceSupport {
  override def configure() = {
    bindActor[RouterActor]("router-manager")
  }
}

根据RouterModule中actor的名称,使用guice DI注入RouterActor

class ScheduledReportGenerationService @Inject() (@Named("router-manager") serviceRouter: ActorRef) extends Actor {
  def receive = {
    case serviceInfo: ServiceContext => submitJobs(serviceInfo)
  }

  def submitJobs(serviceInfo: ServiceContext) = {
    serviceRouter ? serviceInfo
  }
}

如何初始化ScheduledReportGenerationService actor以便Guice自动注入RouterActor?我收到以下错误

java.lang.IllegalArgumentException: no matching constructor found on class vistoscheduler.ScheduledReportGenerationService for arguments []

我理解system.actorOf使用Akka来创建Actor,因此Guice无法注入依赖项 . 文档不是很清楚,我无法解决这个问题 .

class SchedulerBootStrap extends Actor {
  implicit val system = context.system
  implicit val timeout = Timeout(5.minute)

  def receive = {
    case "BOOTSTRAP_SCHEDULER" => bootStrapServices()
  }

  def bootStrapServices() = {
    lazy val scheduledReportGenerationService =    system.actorOf(Props[ScheduledReportGenerationService], "scheduled-reports-service-actor")
    scheduledReportGenerationService ? ServiceContext(1L)
  }
}

回答(1)

2 years ago

你可以使用 IndirectActorProducer

class GuiceActorProducer(val injector: play.inject.Injector, val cls: Class[_ <: Actor]) extends IndirectActorProducer {

  override def actorClass = classOf[Actor]

  override def produce() = {
    injector.instanceOf(cls)
  }

}

并创建您的服务,如,

val scheduledReportGenerationService = system.actorOf(Props(classOf[GuiceActorProducer], injector, classOf[ScheduledReportGenerationService]))

请确保您使用的是 play.inject.Injector .

Edit to add the working codecom.google.inject.Injector .

class Application @Inject() (injector: Injector, system: ActorSystem) extends Controller {

  def index = Action { request =>
    val scheduledReportGenerationService = system.actorOf(Props(classOf[GuiceActorProducer], injector, classOf[ScheduledReportGenerationService]))
    scheduledReportGenerationService ! "some"
    Ok(views.html.index("Your new application is ready."))
  }

}

class RouterActor extends Actor with ActorLogging {
  def receive = {
    case x: String =>
      log.info(x)
  }
}

class ScheduledReportGenerationService @Inject() (@Named("router-manager") serviceRouter: ActorRef) extends Actor {
  implicit val timeout = Timeout(5.minute)
  def receive = {
    case serviceInfo: String => submitJobs(serviceInfo)
  }

  def submitJobs(serviceInfo: String) = {
    log.info("service")
    serviceRouter ? serviceInfo
  }
}

class RouterActorModule extends AbstractModule with AkkaGuiceSupport {
  override def configure() = {
    bindActor[RouterActor]("router-manager")
  }
}



class GuiceActorProducer(val injector: Injector, val cls: Class[_ <: Actor]) extends IndirectActorProducer {

  override def actorClass = cls

  override def produce() = {
    injector.getInstance(cls)
  }
}

使用akka Extension 还有另一种方法可以做到这一点 . 见this project .