简体   繁体   English

使用play.api.cache.Cache和编译时依赖注入

[英]Using play.api.cache.Cache with compile time dependency injection

I am trying to migrate my Play application from 2.3.9 to 2.4.3 and am using compile time dependency injection. 我正在尝试将我的Pl​​ay应用程序从2.3.9迁移到2.4.3并使用编译时依赖注入。 I am getting an InstantiationException when using the old global Cache api object ( play.api.cache.Cache ). 我在使用旧的全局Cache api对象( play.api.cache.Cache )时收到InstantiationException I have included EhCacheComponents in my components (which provides a cache implementation) but it seems Play is trying to instantiate the abstract CacheApi directly: 我在我的组件中包含了EhCacheComponents (它提供了一个缓存实现),但似乎Play试图直接实例化抽象CacheApi

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[InstantiationException: play.api.cache.CacheApi]]

<snip>

Caused by: java.lang.InstantiationException: play.api.cache.CacheApi
at java.lang.Class.newInstance(Class.java:427) ~[na:1.8.0_51]
at play.api.inject.NewInstanceInjector$.instanceOf(Injector.scala:49) ~[play_2.11-2.4.3.jar:2.4.3]
at play.api.inject.SimpleInjector$$anonfun$instanceOf$1.apply(Injector.scala:85) ~[play_2.11-2.4.3.jar:2.4.3]

I am aware that the recommendation is to use the new dependency injected components, but the documentation suggests this should still work, and I would like to get my application running without having to change it all in one go. 我知道建议是使用新的依赖注入组件,但是文档建议这应该仍然有效,并且我希望我的应用程序无需一次性更改即可运行。

Here is a simplified application which demonstrates the problem: 这是一个简化的应用程序,演示了这个问题:

class AppApplicationLoader extends ApplicationLoader {
  def load(context : play.api.ApplicationLoader.Context) : play.api.Application = {
    Logger.configure(context.environment)
    new AppComponents(context).application
  }
}

class AppComponents(context : play.api.ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) with EhCacheComponents {
  lazy val assets = new controllers.Assets(httpErrorHandler)
  lazy val router: Router = new Routes(httpErrorHandler, assets, new controllers.TestController())
}

- -

package controllers

class TestController extends Controller {
    def test = Action {
        Cache.getAs[String]("hello") map { result => 
            Ok(result)
        } getOrElse {
            Ok("not found")
        }
    }
}

Configuration: 组态:

# key, langs, etc. removed
play.application.loader = "AppApplicationLoader"
play.modules.enabled += "play.api.cache.EhCacheModule"

How can I make this work? 我怎样才能做到这一点?

It is possible to do this by replacing the default injector and adding more components. 可以通过替换默认注入器并添加更多组件来实现此目的。 I suppose this is no longer compile-time DI (as dependencies are now being resolved up at runtime), but it works. 我想这不再是编译时的DI(因为依赖关系现在正在运行时解析),但它确实有效。

When extending BuiltInComponents : 扩展BuiltInComponents

trait AppComponents(context: Context) extends BuiltInComponents 
  with I18nComponents 
  with EhCacheComponents {

    // other dependencies (e.g. router, assets) here

    //need to add any other components here that you want to reference via the global APIs  - 
    //e.g. csrfConfig from CSRFComponents      
    override lazy val injector: Injector = new SimpleInjector(
      NewInstanceInjector
    ) + router + crypto + httpConfiguration + defaultCacheApi  + messagesApi
}

Unfortunately you cannot reference super.injector because it is a lazy val , so you are forced to redefine what is already in BuiltInComponents , which isn't great. 不幸的是你不能引用super.injector因为它是一个lazy val ,所以你不得不重新定义BuiltInComponents已有的BuiltInComponents ,这不是很好。 When upgrading Play in future it would be important to check that any new components added to the base definitions are copied to the new implementation. 在将来升级Play时,检查添加到基本定义的任何新组件是否都会复制到新实现中非常重要。

In my actual application I am using MacWire , so I have written a custom injector: 在我的实际应用程序中,我使用的是MacWire ,因此我编写了一个自定义注入器:

class MacwireInjector(fallback: Injector, wired: Wired) extends Injector {

  /**
    * Get an instance of the given class from the injector.
    */
  def instanceOf[T](implicit ct: ClassTag[T]) = instanceOf(ct.runtimeClass.asInstanceOf[Class[T]])

  /**
    * Get an instance of the given class from the injector.
    */
   def instanceOf[T](clazz: Class[T]) = wired.lookup(clazz) match {
    case instance :: Nil => instance
    case Nil => fallback.instanceOf(clazz)
    case set => throw new RuntimeException(s"Multiple instances returned for $clazz: $set")
  }

  /**
    * Get an instance bound to the given binding key.
    */
  def instanceOf[T](key: BindingKey[T]) = instanceOf(key.clazz)
}

BuiltInComponents: BuiltInComponents:

// probably need to do this otherwise MacWire finds two candidates from EhCacheComponents
lazy val cacheApi = defaultCacheApi

override lazy val injector: Injector = new MacwireInjector(NewInstanceInjector, wiredInModule(this))

or using the default injector with MacWire as a fallback: 或使用MacWire的默认注入器作为后备:

override lazy val injector: Injector = new SimpleInjector(
  new MacwireInjector(NewInstanceInjector, wiredInModule(this))
) + router + crypto + httpConfiguration

If you use Compile Time dependency injection, you should pass the dependencies via the parameters of your objects: 如果使用Compile Time依赖项注入,则应通过对象的参数传递依赖项:

class TestController(cache: CacheApi) extends Controller {
  ...
}

And pass the implementation in the app loader: 并在app loader中传递实现:

class AppComponents(context : play.api.ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) with EhCacheComponents {
  lazy val assets = new controllers.Assets(httpErrorHandler)
  lazy val controller = new controllers.TestController(defaultCacheApi)
  lazy val router: Router = new Routes(httpErrorHandler, assets, controller)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Play 2.4 WebSocket在使用编译时依赖项注入时抛出InstantiationException - Play 2.4 WebSocket throws InstantiationException when using compile-time dependency injection Play 框架编译时依赖注入和单例 - Play framework compile-time dependency injection and singleton 编译时依赖项注入项目中的EvolutionsComponents - EvolutionsComponents in compile-time dependency injection play project 同时在Play Framework中使用两个缓存提供程序? - Using two cache providers in Play Framework at the same time? Scala依赖注入用于单独配置的编译时间 - Scala Dependency Injection for compile time with separate configuration 使用Play缓存API存储用户会话ID? - Using the Play cache API for storing user session ids? 使用Play框架管理外部api调用的缓存解决方案 - Cache solution to manage external api calls using play framework play.api.cache.Cache.getOrElse函数是否安全? - Is the play.api.cache.Cache.getOrElse function Thread safe? 播放2.6:缓存Api和内存使用配置 - Play 2.6: Cache Api and Memory Usage Configuration 播放/标量中的依赖注入,无需使用构造函数注入 - Dependency Injection in play/scala without using constructor injection
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM