【问题标题】:Why am I getting this IllegalArgumentException exception when trying to inject services into an actor?为什么我在尝试将服务注入演员时会收到此 IllegalArgumentException 异常?
【发布时间】:2015-10-12 15:38:04
【问题描述】:

我的代码:

Global.scala - 我只是在启动时设置它,它会向参与者发送一条快速消息。从这里抛出异常;我验证了注入的服务已经加载。

object Global extends GlobalSettings {
  override def onStart(app: Application): Unit = {
    val system = app.actorSystem
    system.actorOf(TempActor.props, TempActor.name) ! "hi hi"
  }
}

TempActor.scala

package actors

class TempActor @Inject() (
    @Named(TestServiceModuleNames.RedisService) redisService: StatusService
  , @Named(TestServiceModuleNames.DynamoDbService) dynamoDbService: StatusService
) extends Actor with ActorLogging {

  override def receive: Receive = {
    case msg: Any =>
      log.info(s"the msg => $msg")
      context.system.shutdown()
  }
}

object TempActor extends NamedActor {
  override def name: String = this.getClass.getSimpleName
  override def props: Props = Props[TempActor]
}

TestServiceModule.scala - 一个 guice 模块,用于加载参与者需要的服务,我确保在 application.conf 中启用该模块

package modules

class TestServiceModule extends AbstractModule with AkkaGuiceSupport {

  val configs = ConfigFactory.load()

  override def configure(): Unit = {

    bind(classOf[StatusService]).annotatedWith(Names.named(TestServiceModuleNames.RedisService)).toInstance(new RedisStatusServiceImpl(new RedisConfig(configs.getString("redis.host"), configs.getInt("redis.port"))))
    bind(classOf[StatusService]).annotatedWith(Names.named(TestServiceModuleNames.DynamoDbService)).toInstance(new DynamoDBStatusServiceImpl(Region.US_EAST_1, configs.getString("dynamo.db.endpoint"), configs.getString("dynamo.db.table.name.status")))
  }
}

object TestServiceModuleNames {
  final val RedisService = "RedisStatusService"
  final val DynamoDbService = "DynamoDbStatusService"
}

应用程序.conf

redis.host="localhost"
redis.port=4242

dynamo.db.endpoint="http://localhost:8000"
dynamo.db.table.name.status="status"

play.modules {
  enabled += "modules.TestServiceModule"
}

play.akka.actor-system="warden"

akka {
  loggers = ["akka.event.slf4j.Slf4jLogger"]
  loglevel = "DEBUG"
}

我的目标是拥有一个由演员处理后端处理的播放应用程序。每个参与者都会对不同的服务有一定的依赖关系,我尝试使用 google Guice 注入这些服务。

我启动应用程序时得到的只是异常跟踪:

java.lang.IllegalArgumentException: no matching constructor found on class actors.TempActor for arguments []

我不确定如何准确解决这个问题...

我正在使用 Play 2.4。

【问题讨论】:

标签: scala playframework akka guice


【解决方案1】:

override def props: Props = Props[TempActor] 行试图调用 TempActor 的零参数构造函数版本,但其中不存在。 Props 背后的 Akka 代码中没有任何内容可以让它了解您正在使用 Google Guice 并相应地创建一个 TempActor。

你可以这样做:

override def props: Props = Props[TempActor] = {
  Injector injector = Guice.createInjector(new TestServiceModule());
  Props(injector.getInstance(TempActor.class))
}

【讨论】:

  • 看起来这样可行!我非常接近于从游戏中删除内置的 guice/actor 系统并使用我自己的自定义系统。
  • 问题:我将如何测试演员?由于注入器在道具中是“硬编码”的,所以模拟服务是不可能的,不是吗?而且因为它在道具中,我会在我的许多演员身上重复这个代码?
  • 我对 Akka 方面的了解比 Guice 方面更多,但是如果在您的情况下效果更好,您可以将注入器传递给 props 方法?
【解决方案2】:

最后,我选择了我认为可行的方法。

我使用https://github.com/rocketraman/activator-akka-scala-guice 中的样板代码来生成具有依赖关系的参与者。

在 Play 应用程序本身中,我没有使用 Play 内置的 actorsystem/guice,而是在全局设置中生成我自己的注入器和 actor 系统。

object Global extends GlobalSettings {

  final val injector = Guice.createInjector(
    new ServiceModule(),
    new ConfigModule(),
    new AkkaModule(),
    new ActorModule()
  )

  final val actorSystem = injector.instance[ActorSystem]
  final val quartzScheduler = QuartzSchedulerExtension.get(actorSystem)
  final val configs = new ConfigProvider().get()

  override def onStart(app: Application): Unit = {
      // onstart logic
  }


  override def onStop(app: Application): Unit = {
    actorSystem.shutdown()
  }
}

我设置项目的方式是有一个主要参与者/主管,它通过“actorSystem.actorOf(..., name)”以正常方式生成。我的主管没有任何依赖项,因为它的工作是接收请求并将它们转发给适当的子actor(使用 Rocketraman 的代码创建)。

【讨论】:

    猜你喜欢
    • 2013-11-08
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 2021-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多