依賴注入定義爲組件之間依賴關係由容器在運行期決定,形象的說即由容器動態的將某個依賴關係注入到組件之中在面向對象編程中,咱們常常處理的問題就是解耦,控制反轉(IoC)就是經常使用的面向對象編程的設計原則,其中依賴注入是控制反轉最經常使用的實現。目標解決當前類不負責被依賴類實例的建立和初始化。javascript
依賴是程序中常見的現象,假設有 A和B都被C耦合依賴着,在 OOP 編程中依賴無處不在。依賴形式有多種表現形式,好比一個類向另外一個類發消息,一個類是另外一個類的成員,一個類是另外一個類的參數。java
class A {} class B { classA: A; constructor() { this.classA = new A(); } } class C { classA: A; classB: B; constructor() { this.classA = new A(); this.classB = new B(); } }
eg: 以用戶調用 API 層打印日誌來講明git
class LoggerService { constructor() { } log(args) { console.log(args) } } class ApiService { constructor ( private readonly logger: LoggerService ) { this.logger.log('api constructor') } public async getMydata () { return { name: 'mumiao', hobby: 'focusing in web'} } } class UserService { constructor ( private readonly api: ApiService, private readonly logger: LoggerService ) { this.logger.log('user constructor') } async getMyhobby () { const { hobby } = await this.api.getMydata() return hobby } } async function Main { const loggerService = new LoggerService() const apiService = new ApiService(loggerService) const userService = new UserService(loggerService, userService) console.log('my hobby is', await userService.getMyhobby()) } Main()
UserService
不該該承載ApiService
和LoggerService
實例的建立。採用依賴注入,UserService
不負責被依賴類的建立和銷燬,而是經過外部傳入api
和logger
對象的方式注入。常見依賴注入方式有三種,本文主要以構造器注入爲例解釋。github
const apiService = Injector.resolve < ApiService > ApiService; const userService = Injector.resolve < UserService > UserService; // returns an instance of , with all injected dependencies
Proxy 與 Reflect 是 ES6 爲了操做對象引入的 API,Reflect 的 API 和 Proxy 的 API 一一對應,而且能夠函數式的實現一些對象操做。另外,使用 reflect-metadata 可讓 Reflect 支持元編程web
Reflect.defineMetaData(metadataKey, metadataValue, target) // 在類上定義元數據 Reflect.getMetaData("design:type", target, propertyKey); //返回類被裝飾屬性類型 Reflect.getMetaData("design:paramtypes", target, propertyKey); //返回類被裝飾參數類型 Reflect.getMetaData("design:returntype", target, propertyKey); // 返回類被裝飾函數返回值類型
function funcDecorator(target, name, descriptor) { // target 指 類的prototype name是函數名 descriptor是屬性描述符 let originalMethod = descriptor.value; descriptor.value = function () { console.log("我是Func的裝飾器邏輯"); return originalMethod.apply(this, arguments); }; return descriptor; } class Button { @funcDecorator onClick() { console.log("我是Func的原有邏輯"); } }
const Injector = (): ClassDecorator => { // es7 decorator return (target, key, descriptor) => { console.log(Reflect.getMetadata("design:paramtypes", target)); // [apiService, loggerService] }; }; @Injector() class userService { constructor(api: ApiService, logger: LoggerService) {} }
// interface.ts type Type<T = any> = new (...args: any[]) => T; export type GenericClassDecorator<T> = (target: T) => void; // ServiceDecorator.ts const Service = (): GenericClassDecorator<Type<object>> => { return (target: Type<object>) => {}; }; // Injector.ts export const Injector = { // resolving instances resolve<T>(target: Type<any>): T { // resolved injections from the Injector let injections = Reflect.getMetadata("design:paramtypes", target) || [], injections = injections.map((inject) => Injector.resolve<any>(inject)); return new target(...injections); }, };
只實現了依賴提取的核心部分,依賴注入還有一個部分是Container容器存儲相關。typescript
@Service() class LoggerService { //... } @Service() class ApiService { constructor ( private readonly logger: LoggerService ) { } } @Service class UserService { constructor ( private readonly api: ApiService, private readonly logger: LoggerService ) { } } async function Main { // jnject dependencies const apiService = Injector.resolve<ApiService>(ApiService); const userService = Injector.resolve<UserService>(UserService); console.log('my hobby is', await userService.getMyhobby()) } Main()
聲明接口及類型:express
export interface ILoggerService {} export interface IApiService {} export interface IUserService {} export default TYPES = { // 惟一依賴標識,建議使用Symbol.for替換類做爲標識符 ILoggerService: Symbol.for("ILoggerService"), IApiService: Symbol.for("IApiService"), IUserService: Symbol.for("IUserService"), };
聲明依賴:編程
import 'reflect-metadata' import { injectable, inject } from 'inversify' @injectable() export class LoggerService implements ILoggerService{ //... } @injectable() export class ApiService implements IApiService{ protected _logger: LoggerService constructor ( private @inject(TYPES.ILoggerService) logger: LoggerService ) { this._logger = logger } }
也可使用 property injection 代替 constructor injection ,這樣就不用聲明構造函數。api
@injectable() export class ApiService implements IApiService { @inject(TYPES.ILoggerService) private _logger: LoggerService; }
@injectable() export class UserService implements IUserService { protected _api: ApiService; protected _logger: LoggerService; constructor ( private readonly @inject(TYPES.IApiService) api: ApiService, private readonly @inject(TYPES.ILoggerService) logger: LoggerService ) { this._api = api this._logger = logger } }
建立並配置一個 Containerapp
... const DIContainer = new container() DIContainer.bind<ApiService>(TYPES.IApiService).toSelf() DIContainer.bind<LoggerService>(TYPES.ILoggerService).toSelf()
解析依賴
import "reflect-matadata"; import { UserService } from "./services"; import DIContainer from "./container"; async function Main() { const userService: UserService = DIContainer.resolve<UserService>( UserService ); console.log("my hobby is", await userService.getMyhobby()); } Main();
An exception:
Error: Missing required @Inject or @multiinject annotation in: argument 0 in class Dom.
import "reflect-metadata"; import { injectable } from "inversify"; @injectable() class Dom { public _domUi: DomUi; constructor(@inject(DomUi) domUi: DomUi) { this._domUi = domUi; } } @injectable() class DomUi { public _dom; constructor(@inject(Dom) dom: Dom) { this._dom = dom; } } @injectable() class Test { public _dom; constructor(@inject(Dom) dom: Dom) { this._dom = dom; } } container.bind<Dom>(Dom).toSelf(); container.bind<DomUi>(DomUi).toSelf(); const dom = container.resolve(Test); // Error!
主要緣由:decorator 被調用時,類尚未聲明,致使inject(undefined)
,InversifyJS 推薦使用 Symboy.for 生成依賴惟一標識符
依賴注入通常都藉助第三方框架來實現,實現須要考慮循環依賴,錯誤處理,容器存儲等。