我目前正在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中有更聪明的方法吗?