源码阅读-SubCut(一)

本文介绍了SubCut依赖注入库的基本使用方法,通过一个简单的示例展示了如何配置绑定及注入过程。文章深入探讨了SubCut的核心组件BindingModule及其子类NewBindingModule的功能与实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    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 ,以后可以拿来用用。


转载于:https://my.oschina.net/myprogworld/blog/206981

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值