You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scala中为类/特质方法注入参数?Play Scala Guice DI问题

Hey there! Let's work through your Guice injection issue with Play Scala 2.5.x (Scala 2.11.11) and that ApplicationAuthorizationHandler based on the action builder pattern. I'll break this down into two parts: fixing the handler injection, and explaining Scala/Guice method-level injection options.

Fixing Injection for ApplicationAuthorizationHandler

First, let's address the core problem: you can't inject another class into your ApplicationAuthorizationHandler because it's likely being instantiated directly (e.g., new ApplicationAuthorizationHandler()) instead of being managed by Guice. Here's how to fix this step by step:

1. Make ApplicationAuthorizationHandler Guice-Manageable

Ensure your handler class uses constructor injection with the @Inject annotation so Guice knows to provide its dependencies:

import javax.inject.Inject
import play.mvc.Request
import scala.concurrent.Future

class ApplicationAuthorizationHandler @Inject()(private val yourOtherClass: YourOtherClass) extends AuthorizationHandler {
  // Your existing authorization logic, now using yourOtherClass
  override def authorize[A](request: Request[A]): Future[Boolean] = {
    // Example: Use yourOtherClass to validate permissions
    yourOtherClass.checkPermissions(request)
  }
}

(Note: Replace YourOtherClass with the actual class you want to inject, and make sure that class also has an @Inject constructor if it has its own dependencies.)

2. Convert Your Action Builder from object to class

If your original action builder was an object (common in examples), you'll need to turn it into a class so Guice can inject the ApplicationAuthorizationHandler into it:

import javax.inject.Inject
import play.api.mvc._
import scala.concurrent.Future

class AuthenticatedAction @Inject()(authHandler: ApplicationAuthorizationHandler, parser: BodyParsers.Default)
  extends ActionBuilder[AuthenticatedRequest, AnyContent] with ActionTransformer[Request, AuthenticatedRequest] {

  override def parser: BodyParser[AnyContent] = parser

  override def transform[A](request: Request[A]): Future[AuthenticatedRequest[A]] = {
    // Use the injected authHandler instead of instantiating it
    authHandler.authorize(request).flatMap { isAuthorized =>
      if (isAuthorized) {
        // Fetch user data and wrap in AuthenticatedRequest
        Future.successful(AuthenticatedRequest(user, request))
      } else {
        Future.failed(new AuthorizationException("Unauthorized"))
      }
    }
  }
}

3. Inject the Action Builder into Your Controller

Now, inject the AuthenticatedAction into your controller instead of referencing it directly as an object:

import javax.inject.Inject
import play.api.mvc._

class SecureController @Inject()(authenticatedAction: AuthenticatedAction, cc: ControllerComponents)
  extends AbstractController(cc) {

  def secureEndpoint = authenticatedAction { request =>
    Ok(s"Hello, ${request.user.username}!")
  }
}

4. Update Routes (If Needed)

If you're referencing the action directly in your routes file, use the @ prefix to indicate it's a dependency-injected action:

GET   /secure   @controllers.SecureController.secureEndpoint()

Scala/Guice Method-Level Injection Options

Now, let's cover how to inject dependencies at the method level in Scala, since you asked about that:

As shown above, this is the standard approach for Guice and Play. Dependencies are injected when the class is instantiated, making them visible in the class signature and easy to test. It's the most maintainable option for most cases.

2. Method Parameter Injection

You can inject dependencies directly into methods using the @Inject annotation. This works if the class is managed by Guice:

import javax.inject.Inject

class SomeService {
  def processRequest(@Inject() validationService: ValidationService, requestData: RequestData): Boolean = {
    validationService.validate(requestData)
  }
}

Note: Guice will inject the validationService parameter only when the method is called on a Guice-created instance of SomeService. This is less common than constructor injection but useful for one-off methods where you don't need the dependency across the entire class.

While possible, field injection is generally discouraged because it hides dependencies and uses mutable fields. But for completeness:

import javax.inject.Inject

class SomeService {
  @Inject private var validationService: ValidationService = _

  def processRequest(requestData: RequestData): Boolean = {
    validationService.validate(requestData)
  }
}

4. Self-Type Annotations for Traits

If you're working with traits (which can't have constructor parameters), use self-type annotations to declare dependencies that implementing classes must provide (via injection):

trait AuthorizationHandler {
  self: YourOtherClass => // Declares that implementing classes must have access to YourOtherClass

  def authorize[A](request: Request[A]): Future[Boolean] = {
    // Use YourOtherClass's methods directly
    checkPermissions(request)
  }
}

// Implementing class injects the dependency
class ApplicationAuthorizationHandler @Inject()(override val yourOtherClass: YourOtherClass) extends AuthorizationHandler with YourOtherClass

Key Takeaway

The main issue with your original setup was likely instantiating ApplicationAuthorizationHandler directly (e.g., in an object action builder) instead of letting Guice manage its lifecycle. By converting action builders to classes and using constructor injection, you'll get full access to Guice's dependency injection capabilities.

内容的提问来源于stack exchange,提问作者NKM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:46:43