inversify-依赖注入
单例、销毁
- constructor 构造函数中初始化属性
- container 属性
- get 方法
- bind 方法
装饰器
装饰器拦截class的构造器调用
装饰器需要泛型
esprima老牌IOC工具
补充知识点
- 元编程
- Reflect
- TS的类型匹配
案例
inversify
BFF
febeacon.com
inversify源码实现
import CreateIoc from './ioc';
const container = new createIoc();
//常量区域
interface ITypes {
[key:string]: Symbol;
}
const TYPES:ITypes = {
indexServices:Symbol.for('indexService')
}
interface IIndexService{
log(msg:string): void;
}
class IndexController{
constructor(@inject(TYPES.indexService) indexService?:IIndexService){
//indexService可能为undefined,加上!可以骗过TypeScript
this.indexService = indexService!;
}
info(){
this.indexService.log('hello SOLID!')
}
}
class IndexService implements IIndexService{
log(msg:string){
console.log(msg)
}
}
//将对应的服务注入到容器
container.bind(TYPES.indexService, () => new IndexService())
//获取构造器的参数
function getParams(fn:Function){
let ast = parseScript(fn.toString());
let node = ast.body[0];
//estree的节点类型 @types/estree
let fnParams:Pattern[] = [];
if(node.type === 'FunctionDeclaration'){
fnParams = node.params;
}
let validParams:string[] = [];
fnParams.forEach((obj)=> {
if(obj.type === 'Identifier'){
validParams.push(obj.name);
}
})
return validParams;
}
function hasKey<O extends Object>(obj:O, key:keyof any):key is keyof O{
return obj.hasOwnProperty(key);
}
function controller<T extends {new (...args: any[]): {/**可以代表函数体或者对象*/}}>(controller: T){
class Controller extends controller{
constructor(...args: any[]){
super(props);
const _parmas = getParams(constroller)
for(identity of _params){
//二次校验,存在某些值才能进行赋值,不能随意扩展对象
if(hasKey(this, identity)){
//this[identity] = contaienr.get(TYPES[identity]);
this[identity] = Reflect.getMetadata(TYPES[identity], contructor)
}
}
}
}
return Controller;
}
function inject(serviceIdentifier:Symbol):Function{
return (target:Function, targetKey:string, index:number) => {
//index参数顺序
//只有为构造函数的时候targetKey才没有值
if(!targetKey){
//在相应的类上赋值会污染类,所以要让类知道注入的别名却不将别名绑定到原始类上
//需要使用元数据 reflect-metadata
//元编程的定义,不是定义到类上的东西,而是定义到JS编程
Reflect.defineMetadata(serviceIdentifier, container.get(serviceIdentifier), target)
}
}
}
IOC
class CreateIoc {
public container:Map<Symbol, {callback:Function}>;
contructor(){
this.container = new Map();
}
get(namespace:Symbol){
let item = this.container.get(namespace);
if(item){
return item.callback();
} else {
throw new Error(`${namespace}暂未找到!`)
}
}
bind(key:Symbol, callback:undefined:function){
this.container.set(key, {
callback,
})
}
}
使用AST解析传进来的controller的参数
借助老牌的esprima,解析参数。 ts类型@types/esprima。parseScript解析AST
//tsconfig
{
"compilerOptions": {
"experimentalDecorators": true,
"strict": true,
"noImplicitThis":true,
"noImplicitAny": true
}
}
完整源代码
//app.ts
import CreateIoc from './ioc';
import 'reflect-metadata';
import { parseScript } from 'esprima';
import { Pattern } from 'estree';
//创建container
const container = new CreateIoc();
interface IIndexService {
log(msg: string): void;
}
interface ITypes {
[key: string]: Symbol
}
const TYPES: ITypes = {
indexService: Symbol.for('indexService')
}
function hasKey<O extends Object>(obj: O, key: keyof any): key is keyof O {
return obj.hasOwnProperty(key);
}
//使用装饰器将services注入到container中
function injectable<T extends { new(...args: any[]): {} }>(sb: Symbol) {
return (service: T) => {
container.bind(sb, () => new service())
}
}
function inject(ServiceIdentifier: Symbol) {
//index参数顺序
//只有为构造函数的时候targetKey才没有值
return (target: Function, targetKey: string, index: number) => {
if (!targetKey) {
//在相应的类上赋值会污染类,所以要让类知道注入的别名却不将别名绑定到原始类上
//需要使用元数据 reflect-metadata
//元编程的定义,不是定义到类上的东西,而是定义到JS编程
//将实例对象绑定到元编程上
Reflect.defineMetadata(ServiceIdentifier, container.get(ServiceIdentifier), target)
}
}
}
//工具方法,用于获取构造器的参数,然后从contaienr中获取到对应的实例进行注入
function getParams(fn: Function) {
let ast = parseScript(fn.toString());
let node = ast.body[0];
//estree的节点类型@types/estree
let fnParams: Pattern[] = [];
//只有传入的是构造器类型才是FunctionDeclaration
if (node.type === 'FunctionDeclaration') {
fnParams = node.params;
}
let validParms: string[] = [];
fnParams.forEach((obj) => {
if (obj.type === 'Identifier') {
validParms.push(obj.name);
}
})
return validParms;
}
//定义controller
function controller<T extends { new(...args: any[]): {} }>(controller: T) {
class Controller extends controller {
constructor(...args: any[]) {
super(args);
//获取容器内的
const _params = getParams(controller);
for (let identity of _params) {
if (hasKey(this, identity)) {
//this[identity] = container.get(TYPES[identity])
//通过元编程的方式获取要注入的实例对象
this[identity] = Reflect.getMetadata(TYPES[identity], controller)
}
}
}
}
return Controller;
}
@injectable(TYPES.indexService)
class IndexService implements IIndexService {
log(msg: string) {
console.log(msg)
}
}
@controller
class IndexController {
private indexService: IIndexService;
//标明那些值是注入的,那些值是构造器传入的
constructor(@inject(TYPES.indexService) indexService?: IIndexService) {
this.indexService = indexService as IIndexService;
}
info() {
this.indexService.log('你好我是ioc!🐂🍺');
}
}
let c = new IndexController();
c.info();
//ioc.ts
class CreateIoc {
public container: Map<Symbol, { callback: Function }>;
constructor() {
this.container = new Map();
}
get(namespace: Symbol) {
let item = this.container.get(namespace);
if (item) {
return item.callback();
} else {
throw new Error(`${namespace}暂未找到!`);
}
}
bind(key: Symbol, callback: Function) {
this.container.set(key, {
callback,
})
}
}
export default CreateIoc;