Scaldi依赖注入与Akka Actors

3

我目前正在探索在Play2.2应用程序中使用Scaldi进行依赖注入。

我已阅读Scaldi网站上的文档,但我不清楚如何与Akka一起使用它。

目前我项目中的内容如下:

Models/ (Daos and case classes)
   User.scala
Services/  (Akka Actors)
   UserService.scala
   ProfileService.scala
Managers/  (Regular Manager Classes)
   UserManager.scala (The Trait Interface)
   UserManagerImpl.scala (An actual implementation)
   UserManagerMock.scala (Mocked version)
   etc..

在UserService.scala中,我将使用UserManager的实例来完成工作:
class UserService extends ServiceActor with Injection
{
    val userManager = inject[UserManager]

    def receive = {
        case Register(email: String, password: String)
    }
}

object UserService extends Service
{
    case class Register(email: String, password: String)

    override protected val actorRef = Akka.system.actorOf(Props[UserService].withRouter(SmallestMailboxRouter(resizer = Some(resizer))))
}

那么根据注入的管理器,如果演员把所有工作委托给管理器,会被模拟吗?

然而,如果管理器需要调用其他服务,而这些服务只是伴生对象呢?或者服务调用其他也通过伴生对象引用的服务呢?

有没有人指点如何将Akka与Scaldi集成?

1个回答

4

您提到,您正在使用伴生object作为服务。我还注意到,您正在object内部创建actor。总的来说,我会劝阻您这样做。Scala(伴生)object只是单例。虽然它们在某些情况下可能有用且合适,但通常被认为是反模式而不是模式,特别是如果您想在应用程序中进行依赖注入或控制反转时。有很多原因,但在这种情况下最重要的原因是:很难模拟它们,很难控制它们的实例化,并且总的来说它们代表了控制反转的相反。

另一个问题是,您正在这些单例对象内部创建actor。actor模型的一个非常重要的方面是监督层次结构。通过在隔离中创建此actor(在您的情况下是UserService),您很可能让guardian actor成为它的监督者,这在大多数情况下都不是您想要的。因此,我建议在另一个actor内创建大多数actor,除了需要成为顶级actor的少数actor。这将确保它们具有正确的监督层次结构。

如果您使用Scaldi,这些想法也是一样的。 scaldi-akka提供了一种方便的方式来注入特定actor的ActorRefProps。以下是一个小例子,展示如何注入普通绑定和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
}

重要的是使用toProviderActor绑定起来。这将确保每次Akka向Scaldi请求某个特定的Actor时,它都会得到新的实例。

现在,如果您想要ReceptionistService成为您的顶级actor,您可以像这样使用它:

implicit val system = inject [ActorSystem]

val receptionist = injectActorRef [ReceptionistService]

receptionist ! DoStuff

在这种情况下,system的守护者演员将是它的监督员。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接