Scala Play依赖注入问题:无法注入类或对象字段
问题分析与解决思路
先梳理下你的场景细节:
- 项目结构:
Application | |--> /ApplicationA | -> /app | -> Filters.scala | -> AuthenticationComponent.scala | -> /build.sbt | |--> /ApplicationB | -> /app | -> Module.scala | -> VanillaAuthenticationUtil.scala | -> /build.sbt | |-> /conf |-> /build.sbt - 依赖关系:ApplicationB依赖ApplicationA,ApplicationA对ApplicationB无感知
- 绑定配置(Module.scala):
class Module extends AbstractModule { override def configure(): Unit = { bind(classOf[AuthenticationUtil]) .annotatedWith(Names.named("authUtil")) .to(classOf[VanillaAuthenticationUtil]).asEagerSingleton() } } - 问题代码:
Filters.scala:
AuthenticationComponent.scala:class Filters @Inject()(corsFilter: CORSFilter) extends HttpFilters { @Inject @Named("authUtil") var authUtil:AuthenticationUtil = _ def filters = Seq(corsFilter, authUtil) }class AuthenticationComponent { @Inject @Named("authUtil") var authUtil:AuthenticationUtil = _ def getAuthUtil():AuthenticationFilter ={ println(authUtil == null) authUtil } } - 现象:
Filters中的authUtil注入正常,但调用AuthenticationComponent.getAuthUtil()时authUtil为null;同时尝试在Scala object中注入字段也为null。
核心原因分析
AuthenticationComponent未被DI容器管理
从HttpFilters和CORSFilter能判断这是Play框架项目,它的依赖注入只会对容器创建的实例做字段注入。如果你的代码是通过new AuthenticationComponent()手动实例化这个类,DI容器根本不知道这个实例的存在,自然不会帮你填充authUtil字段。而Filters是Play要求的特殊组件,会被框架自动从DI容器中获取实例,所以注入正常。Scala Object无法被DI容器注入
Scala的object是JVM类加载时自动初始化的单例,它的实例完全不受DI容器控制。DI容器只能管理自己创建的对象,所以你在object里标记@Inject的字段永远不会被填充,始终为null。
解决思路
1. 修复AuthenticationComponent的注入问题
不要手动实例化AuthenticationComponent,而是通过依赖注入的方式获取它的实例:
- 如果你在另一个类中需要使用
AuthenticationComponent,直接通过构造函数注入:class SomeService @Inject()(authComponent: AuthenticationComponent) { // 这里使用authComponent,它的authUtil已经被DI注入 def doSomething() = { val util = authComponent.getAuthUtil() // ... } } - 如果必须在非DI管理的代码中获取它(比如某些工具类),可以用Play的DI提供者手动获取:
不过这种方式不推荐,尽量让所有需要依赖注入的组件都由容器管理。import play.api.inject.Injector import play.api.Play.current val injector = current.injector val authComponent = injector.instanceOf[AuthenticationComponent] val util = authComponent.getAuthUtil()
2. 修复Scala Object的注入问题
放弃在object中使用@Inject,改用以下方案:
- 将逻辑移到class中:把原本在
object里的代码改成class,通过DI注入使用,这是最规范的做法。 - 使用Lazy Val结合DI提供者:如果必须保留
object,可以通过lazy val延迟获取DI实例:
注意:这种方式依赖Play的全局object AuthUtils { private lazy val injector = play.api.Play.current.injector lazy val authUtil: AuthenticationUtil = injector.instanceOf[AuthenticationUtil @Named("authUtil")] }current,在Play 2.6+中推荐使用Application实例来获取injector,避免全局状态依赖。
额外检查点
- 确保
AuthenticationComponent被DI容器扫描到:检查conf/application.conf中是否启用了自动扫描,或者是否在Module中显式绑定了AuthenticationComponent(Play默认会扫描classpath下带@Inject的类)。 - 确认类型匹配:你的
getAuthUtil()方法返回类型是AuthenticationFilter,但注入的是AuthenticationUtil,要确保这两个类型兼容(比如VanillaAuthenticationUtil同时实现了这两个接口),避免后续出现类型转换问题。
内容的提问来源于stack exchange,提问作者tcafrin22
相关产品推荐
相关产品推荐

