如何将参数注入Scala中的类/特征方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何将参数注入Scala中的类/特征方法相关的知识,希望对你有一定的参考价值。

我的Play Scala(2.5x,2.11.11)应用程序中的代码到目前为止运行得很好(它基于以下链接:https://fizzylogic.nl/2016/11/27/authorize-access-to-your-play-application-using-action-builders-and-action-functions/)。但是现在我需要将另一个类实例传递给ApplicationAuthorizationHandler类(注意:在我的代码中,我使用Guice DI将参数注入到类构造函数中)。

当前代码:

class ApplicationAuthorizationHandler
   extends AuthorizationHandler {
...
}

trait AuthorizationHandler {
...
}

trait AuthorizationCheck {
   def authorizationHandler: AuthorizationHandler = new ApplicationAuthorizationHandler

   object AuthenticatedAction extends ActionBuilder[RequestWithPrincipal] {
      override def invokeBlock[A](request: Request[A], block: (RequestWithPrincipal[A]) => Future[Result]): Future[Result] = {
         def unauthorizedAction = authorizationHandler.unauthorized(RequestWithOptionalPrincipal(None, request))
         def authorizedAction(principal: Principal) = block(RequestWithPrincipal(principal, request))

         authorizationHandler.principal(request).fold(unauthorizedAction)(authorizedAction)
      }
  }
}

//Example controller using this trait AuthorizationCheck
class MyController @Inject() extends Controller with AuthorizationCheck {
    def myAction = AuthenticatedAction { implicit request =>
...
}

期望的代码:

class ApplicationAuthorizationHandler @Inject() (userService: UserService)
   extends AuthorizationHandler {
   ...
   // userService is used here
}

但是由于ApplicationAuthorizationHandler的实例在trait AuthorizationCheck中实例化,我无法将UserService实例注入其中。我是Mixin这个特性与所有控制器,所以希望保持相同的方式,除非有更好的方式(并且必须)。首先,有没有办法直接注入class / trait方法?或者,有没有一种方法我不在Trait AuthorizationCheck中实例化ApplicationAuthorizationHandler并在控制器内的运行时传递它?还是其他任何方式?

答案

特征不需要提供实现,因此您可以使用以下内容:

trait AuthorizationHandler {
  ...
}

class ApplicationAuthorizationHandler extends AuthorizationHandler {
  ...
}

trait AuthorizationCheck {

  // just declaring that implementations needs to provide a 
  def authorizationHandler: AuthorizationHandler 

  object AuthenticatedAction extends ActionBuilder[RequestWithPrincipal] {
    override def invokeBlock[A](request: Request[A], block: (RequestWithPrincipal[A]) => Future[Result]): Future[Result] = {
      def unauthorizedAction = authorizationHandler.unauthorized(RequestWithOptionalPrincipal(None, request))
      def authorizedAction(principal: Principal) = block(RequestWithPrincipal(principal, request))

      authorizationHandler.principal(request).fold(unauthorizedAction)(authorizedAction)
    }
  }
}

// So, now this controller needs to provide a concrete implementation 
// of "authorizationHandler" as declared by "AuthorizationCheck".
// You can do it by injecting a "AuthorizationHandler" as a val with
// name authorizationHandler.
class MyController @Inject()(val authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {

   def myAction = AuthenticatedAction { implicit request =>
     ...
   }
}

当然,您需要提供一个模块来将AuthorizationHandler绑定到ApplicationAuthorizationHandler

import play.api.inject._

class AuthorizationHandlerModule extends SimpleModule(
  bind[AuthorizationHandler].to[ApplicationAuthorizationHandler]
)

当然,ApplicationAuthorizationHandler可以注入自己的依赖项。你可以see more details at our docs

另一答案

在许多情况下,你不能使用@Inject guice方法。当在特质和演员之间需要依赖时,这是真的。

我在这些情况下使用的方法是将注射器放在一个物体中

object Injector {
  val injector = Guice.createInjector(new ProjectModule())
}

由于上面是一个对象,你可以从任何地方访问它。 (它像一个单身人士)。

现在,当你需要用户服务时,你的特质或演员

trait Foo {
   lazy val userService = Injector.injector.getInstance(classOf[UserService])
}

不要忘记使变量变为惰性,因为您希望在创建注入器时尽可能晚地创建实例。

以上是关于如何将参数注入Scala中的类/特征方法的主要内容,如果未能解决你的问题,请参考以下文章

如何将 View 类中的代码片段移动到 OnAppearing() 方法?

如何将参数传递给scala中的方法

Scala - 特征中的模板方法模式

在 Scala 中混合多个特征

Java8新特征之Lambda

Android 仪器测试中的 Dagger 2 注入