带你学习inversify.js系列 - inversify基础知识学习(二)
3、inversify的绑定过程
上一小节说的是依赖是如何从容器中取出来的,那么这一小节我们说说依赖是如何注入进去的,以及inversify提供了多少种类型绑定方法。
初学inversify的人很容易被他的绑定语法搞蒙圈,到处都是bind().toXX.whenXXX().
。其实主要是你不清楚这些方法的含义,导致你很难接受这种一目了然的写法。
那么我们首先来说说这些写法的意义。
下图是inversify根据绑定后的操作不同分门别类的所有文件:
从文件命名来看,我们知道inversify提供了诸如to
、when
、on
这类的方法供我们决定一个类型绑定的诸多属性。
接下来记住这句话: 除了to语法,其余的语法其实都是在往Binding这个类实例的属性赋值
就是下面的这些属性:
所有的入口都是指向BindingToSyntax这个类,再往外衍生出各种when
语法。
BindingToSyntax
:指定class绑定到容器内的类型,从写法来说:container.bind(A).toXXX()
,很好理解成绑定类A为(to)XXX。这里的XXX可以有以下几种:- to():必须传入一个构造器,定义的类型是:
BindingTypeEnum.Instance
,后续在使用的时候会new
掉这个构造器 - toSelf():to写法的一种简写方式,内部最后还是调用to
- toConstantValue():绑定为常量,传入的是一个初始化后的实例,定义的类型是:
BindingTypeEnum.ConstantValue
- toDynamicValue():绑定为动态值,在获取的时候会去执行对应的函数,定义的类型是:
BindingTypeEnum.DynamicValue
- toConstructor():绑定为构造函数,在获取之后需要自己实例化,定义的类型是:
BindingTypeEnum.Constructor
- toFactory():绑定为工厂函数,与刚才的动态值不一样,动态值会执行完动态函数返回值,而工厂函数则会返回一个高阶函数,允许你进一步定制值,定义的类型是:
BindingTypeEnum.Factory
- toFunction():绑定为函数,其实就是toConstantValue的别名,定义的类型为:
BindingTypeEnum.Function
- toAutoFactory():绑定为自动工厂函数,此时的工厂函数不用开发者提供,内部自己实现掉了,定义的类型为:
BindingTypeEnum.Factory
- toProvider():绑定为一个异步的工厂函数,称之为Provider,对于需要一些异步操作的时候这种方式非常有用,定义的类型为:
toProvider
- toService():绑定为一个服务,让其解析为以前声明过的别的类型绑定,这个绑定很特殊,没有别的任何后续操作,因为它没有返回值,看代码就知道:
- to():必须传入一个构造器,定义的类型是:
1public toService(service: string | symbol | interfaces.Newable<T> | interfaces.Abstract<T>): void { 2 this.toDynamicValue( 3 (context) => context.container.get<T>(service) 4 ); 5 }
- 上面的所有绑定除了最后一个,都会返回一个
when/on/in
语法供开发者往绑定里面加入更多的元素,比如一些限制条件、指定生效scope等等,接下来的演变如下图,只有to
和toDynamicValue
才支持in
操作,所有其走的路线是inWhenOn
,其余的都是WhenOn
路线:
从上图可以看出所有的调用终结于onActivation。上图的箭头给出了对应的调用关系。
而上面所讲的Binding
类中赋值得到的类型和别的参数,在解析依赖的时候就会这么被用到:
将这张图片结合上面的各种to
语法,你就能理解各种写法的含义了。
4、Inversify-binding-decorators和inversify-inject-decorators介绍
为了更好更丝滑地使用inversify,社区提供了下面两个工具库,来简化很多写法。
4.1、inversify-inject-decorators
该工具库主要提供了lazyInject
之类的方法,除了字面上所说的惰性,另外一个非常重要的功能就是允许你将inversifyJs集成到任何自己控制类实例创建的库或者框架,比如react。什么意思呢?
比如下面这个例子:
1import { inject, injectable, named, Container } from 'inversify' 2import 'reflect-metadata' 3 4interface Weapon {} 5 6@injectable() 7class Katana implements Weapon {} 8 9@injectable() 10class Shuriken implements Weapon {} 11 12interface Ninja { 13 katana: Weapon; 14 shuriken: Weapon; 15} 16 17class Ninja implements Ninja { 18 @inject("Weapon") @named("strong") 19 public katana: Weapon; 20 @inject("Weapon") @named("weak") 21 public shuriken: Weapon; 22 public constructor() {} 23} 24 25const container = new Container() 26 27container.bind<Weapon>("Weapon").to(Katana).whenTargetNamed("strong"); 28container.bind<Weapon>("Weapon").to(Shuriken).whenTargetNamed("weak"); 29 30const ninja: Ninja = new Ninja() 31 32console.log(ninja.katana)
如果Ninja这个类不受我们控制,自己初始化了,但是又需要用到我们容器里存在的Weapon
绑定,那么这个时候上面的写法是获取不到任何绑定的,只会返回undefined。
这个时候就可以考虑使用inversify-inject-decorators
这个库,改写Ninja的实现:
1class Ninja implements Ninja { 2 3 @lazyInjectNamed("Weapon", "strong") 4 public katana: Weapon; 5 6 @lazyInjectNamed("Weapon", "weak") 7 public shuriken: Weapon; 8 9 public constructor() {} 10}
这样就可以获取到Weapon
这个绑定了。实现的原理其实很简单,在调用getDecorators
的时候把container传值进去,之后改写装饰的属性或者参数的setter和getter函数,利用container.get获取值并缓存起来,这样就完成了整个包的实现。至于第一个例子中获取不到值是因为**`injectable和inject其实都只是添加元数据,从而辅助容器在注入绑定和解析绑定的时候提供一定的信息,本质上的取值并不靠这个修饰器**
它提供的几种写法在Inversify上有其对应的:
- @inject => @lazyInject
- @inject @named => @lazyInjectNamed
- @inject @tagged => @lazyInjectTagged
- @multiInject => @lazyMultiInject
4.2、 inversify-binding-decorators
如果说上面的那个工具库是为了hack某些东西,那么这个库纯粹就是为了写更少的代码。直接引用官网的图片:
如果用原生inversify的话,需要写右边一大堆代码,如果引用这个库,那么一个修饰器就可以搞定了。最后再统一调用container.load(buildProviderModule());
原理其实也很简单,就是利用@provide
收集所有的需要绑定的注入,然后统一传给container.load
这个可以接受数组的方法。
除了provide
,还提供了fluentProvider
,后面这个可以支持原先在上一小节说的那些when/on/in的语法。比如下面这样的:
1@fluentProvide(TYPE.Weapon).whenTargetTagged("throwable", true).done(); 2class Katana implements Weapon { 3 public hit() { 4 return "cut!"; 5 } 6}
我们平时使用的时候,更喜欢将这个抽象成一个函数,比如:
1const provideThrowable = function(identifier, isThrowable) { 2 return provide(identifier) 3 .whenTargetTagged("throwable", isThrowable) 4 .done(); 5}; 6@provideThrowable(TYPE.Weapon, true) 7class Katana implements Weapon { 8 public hit() { 9 return "cut!"; 10 } 11}
这次的文章不是单纯地想介绍inversify,而是因为我们即将隆重介绍新的一代spring版nodejs框架,而该框架用到了inversify,有了这篇基础,那么在学习该框架的时候,就不会显得那么捉襟见肘了~
期待我的下篇文章吧:《初识malagu框架》
公众号关注一波~
![微信公众号](https://blogimage.5udou.cn/homePage/%E5%85%AC%E4%BC%97%E5%8F%B7-%E7%99%BD%E8%89%B2%E7%89%88.png?x-oss-process=style/addWaterMarkBottom)
网站源码:linxiaowu66 · 豆米的博客
Follow:linxiaowu66 · Github
关于评论和留言
如果对本文 带你学习inversify.js系列 - inversify基础知识学习(二) 的内容有疑问,请在下面的评论系统中留言,谢谢。