第二十章 依赖注入¶
Play框架支持运行时依赖注入和编译时依赖注入。
运行时依赖注入是指依赖路径在程序运行时才会创建,如果没有找到相应依赖,运行时并不会报错。
运行时依赖注入¶
如果你有一个组件,比如 controller
,依赖于其他组件,这种情况可以是使用 @Inject
注解来声明。
@Inject
注解可以用在字段或者构造器中,推荐使用在构造器中。
import javax.inject._
import play.api.libs.ws._
class MyComponent @Inject() (ws: WSClient) {
// ...
}
注意 @Inject
注解必须位于类名之后,构造器参数之前,并且必须有 ()
。
除此之外,``Guice``还支持其他几种依赖注入方式,但是构造器注入是最简洁的方式。
依赖注入控制器¶
有两种方式使用依赖控制器。
- 注入路由生成器
- 静态路由生成器
默认情况下,Play会将路由对象的 controller
声明为依赖,如果需要显示声明这个依赖,可以在 build.sbt
中进行配置:
routesGenerator := InjectedRoutesGenerator
通过在 build.sbt
中进行如下配置,声明静态路由生成器:
routesGenerator := StaticRoutesGenerator
推荐使用注入路由生成器。
组件声明周期¶
依赖注入管理被注入组件的生命周期,当需要这些组件的时候,才会创建,然后注入到其它组件中。
组件生命周期工作方式如下:
- 当需要组件时进行创建,如果被使用多次,默认将会创建多个实例,如果你需要的是一个单例,需要将组件标记为
singleton
。 - 组件实例都是懒创建的,只有在需要的时候才会创建。
- 组件实例都会自动清除,它组件不再被引用时,将自动清除。
Singletons¶
有时候我们只需要创建一个对象实例,比如数据库连接,这时可以使用 @Singleton
注解实现:
import javax.inject._
@Singleton
class CurrentSharePrice {
@volatile private var price = 0
def set(p: Int) = price = p
def get = price
}
清除¶
有些组件在Play关闭之后需要做一些清理工作,这可以通过 ApplicationLifecycle
组件实现:
import scala.concurrent.Future
import javax.inject._
import play.api.inject.ApplicationLifecycle
@Singleton
class MessageQueueConnection @Inject() (lifecycle: ApplicationLifecycle) {
val connection = connectToMessageQueue()
lifecycle.addStopHook { () =>
Future.successful(connection.stop())
}
//...
}
注意,必须确保注册了stop钩子的类为单例类,否则容易发生内存泄露。
处理循环依赖¶
循环依赖发生在如下情况:
import javax.inject.Inject
class Foo @Inject() (bar: Bar)
class Bar @Inject() (baz: Baz)
class Baz @Inject() (foo: Foo)
可以使用 Provider
解决这个问题:
import javax.inject.{ Inject, Provider }
class Foo @Inject() (bar: Bar)
class Bar @Inject() (baz: Baz)
class Baz @Inject() (foo: Provider[Foo])