50行代码简单实现依赖注入

540 阅读1分钟

用reflect-metadata实现简单依赖注入!

import "reflect-metadata";
import { Type } from '../type';
const ListMetadata = '__list_metadata_';
// 缓存
const listCache: Map<Type<any>, any> = new Map();
// 错误
export class ListError extends Error {
    constructor(msg: string, public type: Type<any>) {
        super(msg);
    }
}
// 依赖注入工厂
class InjectorFactory {
    private _list: Type<any>[] = [];
    constructor(private type: Type<any>, ...args: Type<any>[]) {
        this._list = args;
    }
    create() {
        const params = [];
        for (let li of this._list) {
            const _li = Reflect.getMetadata(ListMetadata, li);
            if (_li) {
                params.push(_li.create());
            } else {
                throw new ListError('Reflect metadata empty', this.type);
            }
        }
        return new this.type(...params);
    }
}
// 装饰器
export function injector(...args: Type<any>[]) {
    return (target: any) => {
        Reflect.defineMetadata(ListMetadata, new InjectorFactory(target, ...args), target);
    }
}
export abstract class IInjector {
    abstract get(key: Type<any>): any;
}
// 静态依赖注入
export class StaticInjector {
    static get(key: Type<any>) {
        if (listCache.has(key)) {
            return listCache.get(key);
        } else {
            try {
                const list = Reflect.getMetadata(ListMetadata, key).create();
                listCache.set(key, list);
                return list;
            } catch (err) {
                throw new ListError('创建失败', key);
            }
        }
    }
}
// 多级依赖注入
export class Injector extends IInjector {
    _list: Map<Type<any>, any> = new Map();
    constructor(private parent?: IInjector) {
        super();
        this.parent = this.parent || { get: StaticInjector.get } as IInjector;
    }
    get(key: Type<any>, _default?: any) {
        if (this._list.has(key)) {
            return this._list.get(key)
        } else {
            let inject = this.parent.get(key);
            if (!inject) {
                inject = _default;
            }
            this._list.set(key, inject);
            return inject;
        }
    }
}

使用

@Injector()
class B{
    constructor(){}
}
@Injector(B)
class A{
    constructor(public b: B){}
}

@Injector(B,A)
class C{
    constructor(
        public b: B,
        public a: A
    ){}
}

StaticInjector.get(C)

改进

import { Record, DependencyRecord, OptionFlags } from './injector';
import { IFunction } from '../../../functor';
export type IResolveRecord = IFunction<Record, any>;
export type IResolveRecordWithRecordMap = IFunction<Map<any, Record>, IResolveRecord>;
export type IResolveDep = IFunction<DependencyRecord, any>;
export type IResolveDepWithRecordMap = IFunction<Map<any, Record>, IResolveDep>;

export type IResolveToken = (token: any) => any;

export const resolveToken = (recordMap: Map<any, Record>) => {
    return (token: any) => {
        const record = recordMap.get(token);
        return resolveRecord(recordMap)(record)
    }
}
export const resolveDepRecord: IResolveDepWithRecordMap = (recordMap: Map<any, Record>) => {
    return (dep: DependencyRecord) => {
        const record = dep.options & OptionFlags.CheckSelf ? recordMap.get(dep.token) : undefined;
        return resolveRecord(recordMap)(record)
    }
}
export const resolveRecord: IResolveRecordWithRecordMap = (recordMap: Map<any, Record>) => {
    return (record: Record) => {
        // 相当于缓存
        if (record.value) {
            return record.value;
        }
        let deps = [];
        if (Array.isArray(record.deps) && record.deps.length > 0) {
            deps = record.deps.map(dep =>
                resolveDepRecord(recordMap)(dep)
            )
        }
        if (record.useNew) {
            return record.value = new (record.fn as any)(...deps)
        } else if (record.fn) {
            return record.value = (record.fn as any)(...record.deps)
        }
    }
}