相当于Play Framework 2.4(Scala)中的Guice + RequestScope

时间:2016-04-20 21:29:09

标签: scala playframework guice

我目前正在Play Framework 2.4中的Controller类中注入一些Service类。每个服务类都引用一个或多个访问数据库的Repository类。它看起来像这样:

class AccountRepositoryImpl @Inject() (db: DB) extends AccountRepository {
    def find(id: Long): Future[Option[Account]] = // ...

    def insert(account: Account): Future[Long] = // ...
}

class AccountServiceImpl @Inject() (
    val accountRepository: AccountRepository) extends AccountService {

    // ...   
}


class AccountController @Inject() (
    val accountService: AccountService,
    val someOtherService: SomeOtherService) extends Controller {

    def signup: Action[JsValue] = Action.async(parse.json) { implicit request =>
        val jsonSignup: JsResult[SignUp] = request.body.validate[SignUp]

        for {
           userid <- accountService.insert(signUp.Value)
           somethingElse <- someOtherService.doMoreSetup(userid)
           // ...
        } yield {
           // ...
        }
}

class ApiModule extends AbstractModule with ScalaModule {

    def configure(): Unit = {
        bind[DB].to[DBImpl]
        bind[AccountRepository].to[AccountRepositoryImpl]
        bind[AccountService].to[AccountServiceImpl]
        bind[SomeOtherService].to[SomeOtherServiceImpl]
    }
} 

这很好用,但现在我想添加跨服务类处理数据库事务的能力。为此,我计划编写一个可以在事务中包装请求的ActionBuilder:

class Transaction @Inject()(
    val db: DB) {

    object TransactionAction extends ActionBuilder[Request] {
        def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]): Future[Result] = {
            db.startTransaction()
            val result = block(request)
            db.commit() // todo: put rollback logic here
            result
        }
    }
}

要做到这一点,我需要在所有其他注入的对象中共享相同的DB对象,而且我没有看到在Guice中轻松做到这一点的方法--- 似乎没有办法在RequestScope中注入单个对象。

所以我的计划是使用Guice的@Assisted符号并创建一些工厂,例如:

class AccountRepositoryImpl @Inject() (@Assisted db: DB) extends AccountRepository {
    // ...
}

trait AccountRepositoryFactory {
   def create(db: DB): AccountRepository
}

class ApiModule extends AbstractModule with ScalaModule {

   def configure(): Unit = {
       // ...

       install(new FactoryModuleBuilder()
           .implement(classOf[AccountRepository],classOf[AccountRepositoryImpl])
           .build(classOf[AccountRepositoryFactory])

       install(new FactoryModuleBuilder()
           .implement(classOf[AccountService],classOf[AccountServiceImpl])
           .build(classOf[AccountServiceFactory])


   )

...然后用工厂替换注入的Traits并将DB注入控制器。

我认为这将允许我在注入的对象中共享一个数据库实例,但它看起来像很多样板,并且编写测试是额外的工作。在Play Framework中有更聪明的方法吗?

0 个答案:

没有答案