Reflect Metadata
简单来说,你可以通过装饰器来给类添加一些自定义的信息// define metadata on an object or property
Reflect.defineMetadata(metadataKey, metadataValue, target);
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);
// get metadata value of a metadata key on the prototype chain of an object or property
let result = Reflect.getMetadata(metadataKey, target);
let result = Reflect.getMetadata(metadataKey, target, propertyKey);
let target = {};
Reflect.defineMetadata("name", "zhufeng", target);
Reflect.defineMetadata("name", "world", target, 'hello');
console.log(Reflect.getOwnMetadata("name", target));
console.log(Reflect.getOwnMetadata("name", target, "hello"));
// apply metadata via a decorator to a constructor
@Reflect.metadata(metadataKey, metadataValue)
class C {
// apply metadata via a decorator to a method (property)
@Reflect.metadata(metadataKey, metadataValue)
method() {}
}
import 'reflect-metadata';
let target = {};
Reflect.defineMetadata('name','zhufeng',target);
Reflect.defineMetadata('name', 'world', target,'hello');
console.log(Reflect.getOwnMetadata('name',target));
console.log(Reflect.getOwnMetadata('name', target, 'hello'));
console.dir(target);
function classMetadata(key,value){
return function(target){
Reflect.defineMetadata(key, value, target);
}
}
function methodMetadata(key, value) {
//target类的原型
return function (target,propertyName) {
//Person.prototype.hello.name=world
Reflect.defineMetadata(key, value, target, propertyName);
}
}
//decorator
//给类本身增加元数据
//@Reflect.metadata('name','Person')
@classMetadata('name', 'Person')
class Person{
//给类的原型增加元数据
//@Reflect.metadata('name', 'world')
@methodMetadata('name', 'world')
hello():string{ return 'world'}
}
console.log(Reflect.getMetadata('name', Person));
console.log(Reflect.getMetadata('name', new Person(),'hello'));
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true
},
"exclude": [
"node_modules",
"dist"
]
}
{
"extends": "./tsconfig.json",
"exclude": [
"node_modules",
"test",
"dist",
"**/*spec.ts"
]
}
export interface Monitor{}
class Monitor27inch implements Monitor{}
interface Host{}
class LegendHost implements Host { }
class Computer{
monitor:Monitor;
host:Host;
constructor(){
this.monitor = new Monitor27inch();
this.host = new LegendHost();
}
startup(){
console.log('组装好了,可以开机了');
}
}
let computer = new Computer();
computer.startup();
interface Monitor{}
class Monitor27inch implements Monitor{}
interface Host{}
class LegendHost implements Host { }
export class Computer{
monitor:Monitor;
host:Host;
constructor(monitor, host){
this.monitor = monitor;
this.host = host;
}
startup(){
console.log('组装好了,可以开机了');
}
}
+let monitor = new Monitor27inch();
+let host = new LegendHost();
+let computer = new Computer(monitor, host);
computer.startup();
控制反转
。在开发中, IoC 意味着你设计好的对象交给容器控制,而不是使用传统的方式,在对象内部直接控制谁依赖了谁,为什么需要依赖,谁注入了谁,注入了什么
cnpm i @nestjs/core @nestjs/common @nestjs/platform-express rxjs reflect-metadata -D
src\main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
src\app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from "./app.controller";
@Module({
controllers: [AppController]
})
export class AppModule {}
src\app.controller.ts
import { Get, Controller } from "@nestjs/common";
@Controller('/')
export class AppController {
@Get('/hello')
hello() {
return 'hello';
}
}
"scripts": {
"start:dev": "nest start --watch"
},
import { Module } from '@nestjs/common';
import { AppController } from "./app.controller";
+import { AppService } from "./app.service";
+import { UseClassLoggerService, UseValueLoggerService, UseValueLoggerServiceStringToken, UseFactoryLoggerService } from "./logger.service";
@Module({
controllers: [AppController],
+ providers: [
AppService,
+ {
+ provide: UseClassLoggerService,
+ useClass: UseClassLoggerService
+ },
+ {
+ provide: UseValueLoggerService,
+ useValue: new UseValueLoggerService()
+ },
+ {
+ provide: 'StringToken',
+ useValue: new UseValueLoggerServiceStringToken()
+ },
+ {
+ provide: 'FactoryToken',
+ useFactory: () => new UseFactoryLoggerService()
+ }
+ ]
+})
export class AppModule {}
src\app.controller.ts
import { Get, Controller,Inject } from "@nestjs/common";
import { AppService } from "./app.service";
+import { UseClassLoggerService, UseValueLoggerService, UseFactoryLoggerService, UseValueLoggerServiceStringToken } from "./logger.service";
@Controller('/')
export class AppController {
constructor(
+ private readonly appService: AppService,
+ private readonly useClassLoggerService: UseClassLoggerService,
+ private readonly useValueLoggerService: UseValueLoggerService,
+ @Inject("StringToken") private readonly useValueLoggerServiceStringToken: UseValueLoggerServiceStringToken,
+ @Inject("FactoryToken") private readonly useFactoryLoggerService: UseFactoryLoggerService
+ ) { }
@Get('/hello')
hello() {
+ this.useClassLoggerService.log('useClassLoggerService');
+ this.useValueLoggerService.log('useValueLoggerService');
+ this.useValueLoggerServiceStringToken.log('StringToken');
+ this.useFactoryLoggerService.log('FactoryToken');
return this.appService.getHello();
}
}
src\app.service.ts
import { Injectable } from "@nestjs/common";
+import { UseClassLoggerService } from "./logger.service";
@Injectable()
export class AppService {
+ constructor(private readonly useClassLoggerService: UseClassLoggerService,){}
getHello(): string {
+ this.useClassLoggerService.log('getHello');
return "Hello";
}
}
src\app.logger.ts
import { Injectable } from "@nestjs/common";
@Injectable()
export class UseClassLoggerService {
constructor(){
console.log('创建 UseClassLoggerService');
}
log(message:string) {
console.log(message);
}
}
export class UseValueLoggerService {
log(message: string) {
console.log(message);
}
}
export class UseValueLoggerServiceStringToken {
log(message: string) {
console.log(message);
}
}
export class UseFactoryLoggerService {
log(message: string) {
console.log(message);
}
}
export interface Type<T>{
new(...args: any[]): T;
}
//class Person{}
//let t:Type<Person> = Person;s
import {Provider,Token} from "./provider";
export class Container {
public providers = new Map<Token<any>, Provider<any>>();
addProvider<T>(provider: Provider<T>) {
this.providers.set(provider.provide, provider);
}
}
provider.ts
import { Type } from "./type";
export class InjectionToken {
constructor(public injectionIdentifier: string) { }
}
//Token 类型是一个联合类型,既可以是一个函数类型也可以是 InjectionToken 类型
export type Token<T> = Type<T> | InjectionToken;
export interface BaseProvider<T> {
provide: Token<T>;
}
export interface ClassProvider<T> extends BaseProvider<T> {
provide: Token<T>;
useClass: Type<T>;
}
export interface ValueProvider<T> extends BaseProvider<T> {
provide: Token<T>;
useValue: T;
}
export interface FactoryProvider<T> extends BaseProvider<T> {
provide: Token<T>;
useFactory: () => T;
}
export type Provider<T> =
| ClassProvider<T>
| ValueProvider<T>
| FactoryProvider<T>;
export * from "./container";
import {Container} from './';
let container = new Container();
const point = { x: 100,y:100 };
class BasicClass { }
// 注册ClassProvider
container.addProvider({ provide: BasicClass, useClass: BasicClass });
// 注册ValueProvider
container.addProvider({ provide: BasicClass, useValue: point });
// 注册FactoryProvider
container.addProvider({ provide: BasicClass, useFactory: () => point });
console.log(container.providers);
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction;
Inject
装饰器属于参数装饰器declare type ParameterDecorator = (target: Object,
propertyKey: string | symbol, parameterIndex: number ) => void
import "reflect-metadata";
const INJECTABLE_METADATA_KEY = Symbol("INJECTABLE_KEY");
export function Injectable() {
return function (target: any) {
Reflect.defineMetadata(INJECTABLE_METADATA_KEY, true, target);
return target;
};
}
import 'reflect-metadata';
import { Token } from './provider';
const INJECT_METADATA_KEY = Symbol('INJECT_KEY');
export function Inject(token: Token<any>) {
return function (target: any, _: string | symbol, index: number) {
Reflect.defineMetadata(INJECT_METADATA_KEY, token, target, `index-${index}`);
return target;
};
}
import {
Provider, Token, InjectionToken,
ClassProvider, ValueProvider,FactoryProvider,
isClassProvider, isValueProvider,isFactoryProvider} from "./provider";
import { Type } from "./type";
export class Container {
public providers = new Map<Token<any>, Provider<any>>();
addProvider<T>(provider: Provider<T>) {
this.providers.set(provider.provide, provider);
}
inject<T>(type: Token<T>): T {
let provider = this.providers.get(type);
return this.injectWithProvider(type, provider);
}
private injectWithProvider<T>(type: Token<T>, provider?: Provider<T>): T {
if (isClassProvider(provider)) {
//TODO
} else if (isValueProvider(provider)) {
return this.injectValue(provider as ValueProvider<T>);
} else if (isFactoryProvider(provider)){
return this.injectFactory(provider as FactoryProvider<T>);
}
}
private injectValue<T>(valueProvider: ValueProvider<T>): T {
return valueProvider.useValue;
}
private injectFactory<T>(valueProvider: FactoryProvider<T>): T {
return valueProvider.useFactory();
}
}
emitDecoratorMetadata
生成的装饰器,用来定义一些特殊元数据design:paramtypes等,这些特殊元数据可以获取编译之前的类型信息tsc params/index.ts --experimentalDecorators --emitDecoratorMetadata --target es5
/**
* 类装饰器
* @param constructor 类的构造函数
*/
function classDecorator(constructor: Function) {}
/**
* 属性装饰器
* @param target 静态成员来说是类的构造函数,对于实例成员是类的原型对象
* @param property 属性的名称
*/
function propertyDecorator(target: any, property: string) {}
/**
* 方法装饰器
* @param target 静态成员来说是类的构造函数,对于实例成员是类的原型对象
* @param property 方法的名称
* @param descriptor 方法描述符
*/
function methodDecorator(target: any, property: string, descriptor: PropertyDescriptor) { }
/**
* 参数装饰器
* @param target 静态成员是类的构造函数,实例成员是类的原型对象
* @param methodName 方法名
* @param paramsIndex 参数在函数列表中的索引
*/
function paramDecorator(target: any, methodName: string, paramsIndex: number) { }
import 'reflect-metadata';
interface Type<T> {
new(...args: any[]): T;
}
class InjectionToken {
constructor(public injectionIdentifier: string) { }
}
type Token<T> = Type<T> | InjectionToken;
const INJECT_METADATA_KEY = 'INJECT_KEY';
function Inject(token: Token<any>) {
return function (target: any, _propertyName: string | symbol, index: number) {
Reflect.defineMetadata(INJECT_METADATA_KEY, token, target, `index-${index}`);
return target;
};
}
class Car { }
class LoggerService { }
class Wife {
constructor(
private car: Car,
@Inject(new InjectionToken('Logger')) private loggerService:LoggerService
) { }
}
console.log(Wife);
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
require("reflect-metadata");
var InjectionToken = /** @class */ (function () {
function InjectionToken(injectionIdentifier) {
this.injectionIdentifier = injectionIdentifier;
}
return InjectionToken;
}());
var INJECT_METADATA_KEY = 'INJECT_KEY';
function Inject(token) {
return function (target, _propertyName, index) {
Reflect.defineMetadata(INJECT_METADATA_KEY, token, target, "index-" + index);
return target;
};
}
var Car = /** @class */ (function () {
function Car() {
}
return Car;
}());
var LoggerService = /** @class */ (function () {
function LoggerService() {
}
return LoggerService;
}());
var Wife = /** @class */ (function () {
function Wife(car, loggerService) {
this.car = car;
this.loggerService = loggerService;
}
Wife = __decorate([
__param(1, Inject(new InjectionToken('Logger'))),
__metadata("design:paramtypes", [Car,
LoggerService])
], Wife);
return Wife;
}());
console.log(Wife);
"use strict";
require("reflect-metadata");
/**
* 装饰器执行器
* @param {*} decorators 装饰器数组
* @param {*} target 装饰器目标
* @param {*} key 元数据键
* @param {*} desc 方法的描述符
*/
var __decorate = function (decorators, target, key, desc) {
/*获取参数长度,当参数长度小于3,说明目标就是target,否则目标为方法描述符
描述符不存在时,通过key从target获取,即认为key是方法名 */
var argsLength = arguments.length,
decoratorTarget =
argsLength < 3
? target
: desc === null
? (desc = Object.getOwnPropertyDescriptor(target, key))
: desc;
/* 如果Reflect的decorate方法存在,则调用这个方法为目标调用装饰器方法数组,这个方法在reflect-metadata包中实现 */
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
decoratorTarget = Reflect.decorate(decorators, target, key, desc);
/*
如果Reflect.decorate方法不存在,则手动调用装饰方法,注意是倒序调用
如果参数长度小于3说明是类装饰器,直接将类传递给装饰器方法
如果参数长度等于3说明是类装饰器,但是key参数存在,与类一同传递给装饰器方法
如果参数长度大于3说明是方法装饰器,将类、key、方法描述符传递给装饰器方法
同时获取装饰器方法执行完毕的target给decoratorTarget,如果装饰器方法执行完毕没有返回值,则使用之前的decoratorTarget */
else {
for (var i = decorators.length - 1; i >= 0; i--) {
let decorator = decorators[i];
if (decorator) {
decoratorTarget =
(argsLength < 3
? decorator(decoratorTarget)
: argsLength > 3
? decorator(target, key, decoratorTarget)
: decorator(target, key)) || decoratorTarget;
}
}
}
/* 返回decoratorTarget,参数小于3时为类对象,参数大于3时为方法描述符,当为描述符时需要重新将其定义到target上 */
return (
argsLength > 3 &&
decoratorTarget &&
Object.defineProperty(target, key, decoratorTarget),
decoratorTarget
);
}
/**
* 装饰器工厂,可以获取在类上定义指定键值对的装饰器,一般用来定义emitDecoratorMetadata
* @param {*} k 属性名
* @param {*} v 属性值
*/
var __metadata = function (k, v) {
return Reflect.metadata(k, v);
};
/**
* 参数装饰器工厂,用来获取参数装饰器
* @param {*} paramIndex 参数下标
* @param {*} decorator 装饰器
* @param {*} target 装饰的目标为类
* @param {*} key 属性名
*/
var __param = function (paramIndex, decorator) {
return function (target, key) {
decorator(target, key, paramIndex);
};
};
var InjectionToken = (function () {
function InjectionToken(injectionIdentifier) {
this.injectionIdentifier = injectionIdentifier;
}
return InjectionToken;
})();
var INJECT_METADATA_KEY = "INJECT_KEY";
/**
* 返回 token装饰器
* @param {*} token 标识符
* @param {*} target 装饰的目标
* @param {*} _propertyName 属性名
* @param {*} index 索引
*/
function Inject(token) {
return function (target, _propertyName, index) {
Reflect.defineMetadata(INJECT_METADATA_KEY,token,target,"index-" + index);
return target;
};
}
var Car = (function () {
function Car() { }
return Car;
})();
var LoggerService = (function () {
function LoggerService() { }
return LoggerService;
})();
var Wife = (function () {
function Wife(car, loggerService) {
this.car = car;
this.loggerService = loggerService;
}
Wife = __decorate(
[
__param(1, Inject(new InjectionToken("Logger"))),
__metadata("design:paramtypes", [Car, LoggerService]),
],
Wife
);
return Wife;
})();
console.log(Reflect.getMetadata(INJECT_METADATA_KEY, Wife, "index-1"));
console.log(Wife);
import {
Provider, Token, InjectionToken,
ClassProvider, ValueProvider,FactoryProvider,
+ isClassProvider, isValueProvider,isFactoryProvider} from "./provider";
+import { getInjectionToken, } from "./inject";
+import { Type } from "./type";
+type InjectableParam = Type<any>;
+const REFLECT_PARAMS = "design:paramtypes";
export class Container {
public providers = new Map<Token<any>, Provider<any>>();
addProvider<T>(provider: Provider<T>) {
this.providers.set(provider.provide, provider);
}
+ inject<T>(type: Token<T>): T {
+ let provider = this.providers.get(type);
+ return this.injectWithProvider(type, provider);
+ }
+ private injectWithProvider<T>(type: Token<T>, provider?: Provider<T>): T {
+ if (provider === undefined) {
+ throw new Error(`No provider for type ${this.getTokenName(type)}`);
+ }
+ if (isClassProvider(provider)) {
+ return this.injectClass(provider as ClassProvider<T>);
+ } else if (isValueProvider(provider)) {
+ return this.injectValue(provider as ValueProvider<T>);
+ } else if (isFactoryProvider(provider)){
+ return this.injectFactory(provider as FactoryProvider<T>);
+ }
+ }
+ private injectValue<T>(valueProvider: ValueProvider<T>): T {
+ return valueProvider.useValue;
+ }
+ private injectFactory<T>(valueProvider: FactoryProvider<T>): T {
+ return valueProvider.useFactory();
+ }
+ private injectClass<T>(classProvider: ClassProvider<T>): T {
+ const target = classProvider.useClass;
+ const params = this.getInjectedParams(target);
+ return Reflect.construct(target, params);
+ }
+ private getInjectedParams<T>(target: Type<T>) {
+ const argTypes = Reflect.getMetadata(REFLECT_PARAMS, target) as (
+ | InjectableParam
+ | undefined)[];
+ if (argTypes === undefined) {
+ return [];
+ }
+ return argTypes.map((argType, index) => {
+ const overrideToken = getInjectionToken(target, index);
+ const actualToken = overrideToken === undefined ? argType : overrideToken;
+ let provider = this.providers.get(actualToken);
+ return this.injectWithProvider(actualToken, provider);
+ });
+ }
+ private getTokenName<T>(token: Token<T>) {
+ return token instanceof InjectionToken
+ ? token.injectionIdentifier
+ : token.name;
+ }
}
import { Type } from "./type";
export class InjectionToken {
constructor(public injectionIdentifier: string) { }
}
//Token 类型是一个联合类型,既可以是一个函数类型也可以是 InjectionToken 类型
export type Token<T> = Type<T> | InjectionToken;
export interface BaseProvider<T> {
provide: Token<T>;
}
export interface ClassProvider<T> extends BaseProvider<T> {
provide: Token<T>;
useClass: Type<T>;
}
export interface ValueProvider<T> extends BaseProvider<T> {
provide: Token<T>;
useValue: T;
}
export interface FactoryProvider<T> extends BaseProvider<T> {
provide: Token<T>;
useFactory: () => T;
}
export type Provider<T> =
| ClassProvider<T>
| ValueProvider<T>
| FactoryProvider<T>;
+export function isClassProvider<T>(
+ provider: BaseProvider<T>
+): provider is ClassProvider<T> {
+ return (provider as any).useClass !== undefined;
+}
+export function isValueProvider<T>(
+ provider: BaseProvider<T>
+): provider is ValueProvider<T> {
+ return (provider as any).useValue !== undefined;
+}
+export function isFactoryProvider<T>(
+ provider: BaseProvider<T>
+): provider is FactoryProvider<T> {
+ return (provider as any).useFactory !== undefined;
+}
ioc\Inject.ts
import 'reflect-metadata';
import { Token } from './provider';
const INJECT_METADATA_KEY = Symbol('INJECT_KEY');
export function Inject(token: Token<any>) {
return function (target: any, _: string | symbol, index: number) {
Reflect.defineMetadata(INJECT_METADATA_KEY, token, target, `index-${index}`);
return target;
};
}
export function getInjectionToken(target: any, index: number) {
return Reflect.getMetadata(INJECT_METADATA_KEY, target, `index-${index}`) as
| Token<any>
| undefined;
}
ioc\Injectable.ts
import "reflect-metadata";
const INJECTABLE_METADATA_KEY = Symbol("INJECTABLE_KEY");
export function Injectable() {
return function (target: any) {
Reflect.defineMetadata(INJECTABLE_METADATA_KEY, true, target);
return target;
};
}
import { Container } from "./container";
import { Injectable } from "./injectable";
import { Inject } from "./inject";
import { InjectionToken } from "./provider";
const HouseStringToken = new InjectionToken("House");
@Injectable()
class Car { }
@Injectable()
class House { }
@Injectable()
class Wife {
constructor(
private car: Car,
@Inject(HouseStringToken) private house: House
) { }
}
const container = new Container();
container.addProvider({
provide: HouseStringToken,
useValue: new House(),
});
container.addProvider({ provide: Car, useClass: Car });
container.addProvider({ provide: Wife, useClass: Wife });
const wife = container.inject(Wife);
console.dir(wife);
cnpm i ts-node typescript reflect-metadata -D
.vscode\launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug ts",
"type": "node",
"request": "launch",
"runtimeArgs": [
"-r",
"ts-node/register"
], //核心
"args": [
"${relativeFile}"
]
}
]
}