为什么在尝试将服务注入 actor 时会出现此 IllegalArgumentException 异常?
Why am I getting this IllegalArgumentException exception when trying to inject services into an actor?
我的代码:
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"
}
application.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"
}
我的 objective 是要有一个由演员处理后端处理的播放应用程序。每个参与者都会对不同的服务有一定的依赖性,我尝试使用 google Guice 注入这些服务。
当我启动应用程序时,我得到的只是异常跟踪:
java.lang.IllegalArgumentException: no matching constructor found on class actors.TempActor for arguments []
我不确定如何准确解决这个问题...
我正在使用 Play 2.4。
行 override def props: Props = Props[TempActor]
试图调用 TempActor 的零参数构造函数版本,其中 none 存在。 Props 背后的 Akka 代码中没有任何内容可以让它理解您正在使用 Google Guice 并相应地创建一个 TempActor。
你可以用类似的东西来做到这一点:
override def props: Props = Props[TempActor] = {
Injector injector = Guice.createInjector(new TestServiceModule());
Props(injector.getInstance(TempActor.class))
}
最后,我选择了我觉得可行的方法。
我使用 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()
}
}
我设置项目的方式是有一个主要的 actor/supervisor,它是通过 "actorSystem.actorOf(..., name)" 以正常方式生成的。我的主管没有任何依赖关系,因为它的工作是接收请求并将它们转发给适当的子演员(使用 rocketraman 的代码创建)。
我的代码:
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"
}
application.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"
}
我的 objective 是要有一个由演员处理后端处理的播放应用程序。每个参与者都会对不同的服务有一定的依赖性,我尝试使用 google Guice 注入这些服务。
当我启动应用程序时,我得到的只是异常跟踪:
java.lang.IllegalArgumentException: no matching constructor found on class actors.TempActor for arguments []
我不确定如何准确解决这个问题...
我正在使用 Play 2.4。
行 override def props: Props = Props[TempActor]
试图调用 TempActor 的零参数构造函数版本,其中 none 存在。 Props 背后的 Akka 代码中没有任何内容可以让它理解您正在使用 Google Guice 并相应地创建一个 TempActor。
你可以用类似的东西来做到这一点:
override def props: Props = Props[TempActor] = {
Injector injector = Guice.createInjector(new TestServiceModule());
Props(injector.getInstance(TempActor.class))
}
最后,我选择了我觉得可行的方法。
我使用 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()
}
}
我设置项目的方式是有一个主要的 actor/supervisor,它是通过 "actorSystem.actorOf(..., name)" 以正常方式生成的。我的主管没有任何依赖关系,因为它的工作是接收请求并将它们转发给适当的子演员(使用 rocketraman 的代码创建)。