SubCut,一个用来依赖注入的lib,先上一个最简单的demo。
import com.escalatesoft.subcut.inject.BindingId
import com.escalatesoft.subcut.inject.NewBindingModule
import com.escalatesoft.subcut.inject.BindingModule
import com.escalatesoft.subcut.inject.Injectable
object Poolsize extends BindingId
object ServerURL extends BindingId
object SomeConfigurationModule extends NewBindingModule(module => {
import module._
bind[String].idBy(ServerURL).toSingle("www.baidu.com")
bind[Int].idBy(Poolsize).toSingle(1)
})
class SomeServeiceOrClass(implicit val bindingModule: BindingModule) extends Injectable {
val a = injectOptional[Int](Poolsize) getOrElse -1
val b = injectOptional[String](ServerURL) getOrElse ""
//val b = injectOptional(ServerURL) 错误,必须有类型,且类型不能为子类或者父类
def p = println(a + b)
}
object Main extends App with Injectable {
implicit val bindingModule = SomeConfigurationModule
println(bindingModule.bindings)//输出当前所有bindings的信息
val demo = new SomeServeiceOrClass
demo.p
}
该开源项目,大量使用了Manifest来进行类型推断以及通过反射来实例化。根据demo看来,和不用SubCut直接使用trait没啥区别,根据官网文档看,"A feature I believe is long overdue in SubCut is the ability to load configuration from files that can be altered without needing re-compilation."
官网上的demo,会有问题,如下:
bind [String] idBy ServerURL to "http://escalatesoft.com"
报错为:
Implicit conversions found: ServerURL => bindingIdToString(ServerURL) //PS:没有研究,谁研究了,麻烦告知一声。
NewBindingModule继承于BindingModule,而BindingModule 提供def bindings: immutable.Map[BindingKey[_], Any]未实现方法,由子类来实现具体的绑定信息存储。NewBindingModule 的bindings实现为 lazy val初始化MutableBindingModule(在immutable.Map.empty[BindingKey[_], Any]基础上,封装了线程安全访问以及是否绑定状态)。
BindingModule里面有很重要的方法:
/**
* Inject for a given class with optional name. Whatever is bound to the class will be provided.
* If the name is given, only a matching class/name pair will be used, and it will not fall back
* to just providing an implementation based only on the class.
* @param name an optional name to use for the binding match
* @return the instance the binding was configured to return
*/
def inject[T <: Any : Manifest](name: Option[String]): T = {
val key = BindingKey(manifest, name)
injectOptional[T](key) match {
case None => throw new BindingException("No binding for key " + key)
case Some(instance) => instance
}
}
/**
* Retrieve an optional binding for class T with the given BindingKey, if no
* binding is available for the binding key, a None will be returned,
* otherwise the binding will be evaluated and an instance of a subclass of T will be
* returned.
* @param key a BindingKey to use for the lookup
* @return Option[T] containing either an instance subtype of T, or None if no matching
* binding is found.
*/
def injectOptional[T](key: BindingKey[T]): Option[T] = {
// common sense check - many binding maps are empty, we can short circuit all lookup if it is
// and just return None
if (bindings.isEmpty) None else {
val bindingOption = bindings.get(key)
if (bindingOption == None) None
else
(bindingOption.get match {
case ip: ClassInstanceProvider[_] => Some(ip.newInstance(this))
case lip: LazyInstanceProvider[_] => Some(lip.instance)
case lmip: LazyModuleInstanceProvider[_] => Some(lmip.instance)
case nip: NewInstanceProvider[_] => Some(nip.instance)
case nbip: NewBoundInstanceProvider[_] => Some(nbip.instance(this))
case i => Some(i)
}).asInstanceOf[Option[T]]
}
}
InjectOptional方法则是根据类型推断 和BindingKey case class 来查找压入MutableBindingModule的数据类型,并包了一层option。
demo中bind方法则用来生成绑定对象Bind,该类中 toProvider、toSingleInstance、toModuleSingle的各个方法的具体含义参考https://github.com/dickwall/subcut/blob/master/GettingStarted.md 即可,各自对应着LazyInstanceProvider、LazyModuleInstanceProvider、NewInstanceProvider、NewBoundInstanceProvider。
上面提到BindingKey是用来记录注入类信息的case class ,源码如下:
private[inject] case class BindingKey[A](m: Manifest[A], name: Option[String])
项目中有个fileParser的封装,PropertyFileModule.scala ,以后可以拿来用用。