您提到,您正在使用伴生object
作为服务。我还注意到,您正在object
内部创建actor。总的来说,我会劝阻您这样做。Scala(伴生)object
只是单例。虽然它们在某些情况下可能有用且合适,但通常被认为是反模式而不是模式,特别是如果您想在应用程序中进行依赖注入或控制反转时。有很多原因,但在这种情况下最重要的原因是:很难模拟它们,很难控制它们的实例化,并且总的来说它们代表了控制反转的相反。
另一个问题是,您正在这些单例对象内部创建actor。actor模型的一个非常重要的方面是监督层次结构。通过在隔离中创建此actor(在您的情况下是UserService
),您很可能让guardian actor成为它的监督者,这在大多数情况下都不是您想要的。因此,我建议在另一个actor内创建大多数actor,除了需要成为顶级actor的少数actor。这将确保它们具有正确的监督层次结构。
如果您使用Scaldi,这些想法也是一样的。
scaldi-akka提供了一种方便的方式来注入特定actor的
ActorRef
或
Props
。以下是一个小例子,展示如何注入普通绑定和
ActorRefs
:
class ProfileManager (implicit inj: Injector) extends Injectable
trait UserManager {
def register(email: String, password: String): User
}
class UserManagerImpl(implicit inj: Injector) extends UserManager with Injectable {
val profileManager = inject [ProfileManager]
def register(email: String, password: String) = ???
}
class UserService(implicit inj: Injector) extends Actor with AkkaInjectable {
val userManager = inject [UserManager]
import UserService._
def receive = {
case Register(email, password) =>
userManager
}
}
object UserService {
case class Register(email: String, password: String)
}
class ReceptionistService(implicit inj: Injector) extends Actor with AkkaInjectable {
val userManager = injectActorRef [UserService]
def receive = ???
}
请注意,
injectActorRef
会在当前actor的上下文中创建一个actor。因此等效的代码应该是:
val userManager = context.actorOf(injectActorProps[UserService])
现在您需要为
ActorSystem
创建绑定(这是可选的,如果您正在使用Play,则可能需要从play应用程序获取
ActorSystem
,该应用程序已经具有一个),服务(在您的情况下是演员)和管理者:
implicit val module = new Module {
bind [ActorSystem] to ActorSystem("MySystem")
binding toProvider new UserService
binding toProvider new ReceptionistService
bind [UserManager] to new UserManagerImpl
binding to new ProfileManager
}
重要的是使用
toProvider
将
Actor
绑定起来。这将确保每次Akka向Scaldi请求某个特定的
Actor
时,它都会得到新的实例。
现在,如果您想要ReceptionistService
成为您的顶级actor,您可以像这样使用它:
implicit val system = inject [ActorSystem]
val receptionist = injectActorRef [ReceptionistService]
receptionist ! DoStuff
在这种情况下,
system
的守护者演员将是它的监督员。