<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          聊聊 nestjs 中的依賴注入

          共 12760字,需瀏覽 26分鐘

           ·

          2022-03-17 11:05

          本文首發(fā)于政采云前端團(tuán)隊(duì)博客:聊聊 nestjs 中的依賴注入

          https://zoo.team/article/nestjs


          前言

          首先 nestjs 是什么?引用其官網(wǎng)的原話 A progressive Node.js framework for building efficient, reliable and scalable server-side applications.,翻譯一下就是:“一個(gè)可以用來搭建高效、可靠且可擴(kuò)展的服務(wù)端應(yīng)用的 node 框架”。目前在 github 上有 42.4k 的 star 數(shù),人氣還是很高的。

          在使用過程中會(huì)發(fā)現(xiàn) nest 框架和后端同學(xué)使用的 Springboot 以及前端三大框架之一的 Angular 都有很多相似之處。沒錯(cuò)這三個(gè)框架都有相似的設(shè)計(jì),并都實(shí)現(xiàn)了依賴注入。

          可能對(duì)大部分前端同學(xué)來說,依賴注入這個(gè)詞還比較陌生,本文就圍繞依賴注入這個(gè)話題,展開討論一下依賴注入是什么?以及在 nestjs 中詳細(xì)的實(shí)現(xiàn)過程。

          重要概念

          概念解釋

          先來看看幾個(gè)重要概念的解釋

          • 依賴倒置原則( DIP ):抽象不應(yīng)該依賴實(shí)現(xiàn),實(shí)現(xiàn)也不應(yīng)該依賴實(shí)現(xiàn),實(shí)現(xiàn)應(yīng)該依賴抽象。
          • 依賴注入(dependency injection,簡(jiǎn)寫為 DI):依賴是指依靠某種東西來獲得支持。將創(chuàng)建對(duì)象的任務(wù)轉(zhuǎn)移給其他class,并直接使用依賴項(xiàng)的過程,被稱為“依賴項(xiàng)注入”。
          • 控制反轉(zhuǎn)(Inversion of Control, 簡(jiǎn)寫為 IoC):指一個(gè)類不應(yīng)靜態(tài)配置其依賴項(xiàng),應(yīng)由其他一些類從外部進(jìn)行配置。

          結(jié)合代碼

          光看上面的解釋可能并不好理解?那么我們把概念和具體的代碼結(jié)合起來看。

          1. 根據(jù) nest 官網(wǎng)教程,用腳手架創(chuàng)建一個(gè)項(xiàng)目,創(chuàng)建好的項(xiàng)目中有 main.ts 文件為入口文件,引入了 app.module.ts 文件,而 app.module.ts 文件引入了 app.controller.ts。先看一下代碼的邏輯:
          ???//?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';
          ???import?{?AppService?}?from?'./app.service';?
          ???
          ???@Module({
          ?????imports:?[],
          ?????controllers:?[AppController],
          ?????providers:?[AppService],
          ???})
          ???export?class?AppModule?{}
          ???//?src/app.controller.ts文件
          ???import?{?Controller,?Get?}?from?'@nestjs/common';
          ???import?{?AppService?}?from?'./app.service';
          ???
          ???@Controller()
          ???export?class?AppController?{
          ?????constructor(private?readonly?appService:?AppService)?{}
          ???
          ?????@Get()
          ?????getHello():?string?{
          ???????return?this.appService.getHello();
          ?????}
          ???}
          ???//?src/app.service.ts文件
          ???import?{?Injectable?}?from?'@nestjs/common';
          ???
          ???@Injectable()
          ???export?class?AppService?{
          ?????getHello():?string?{
          ???????return?'Hello?World!';
          ?????}
          ???}
          ???

          現(xiàn)在我們執(zhí)行 npm start 啟動(dòng)服務(wù),訪問 localhost:3000 就會(huì)執(zhí)行這個(gè) AppController 類中的 getHello 方法了。我們來看 app.controller.ts 文件。可以看到構(gòu)造函數(shù)的參數(shù)簽名中第一個(gè)參數(shù) appService 是 AppService 的一個(gè)實(shí)例。

          constructor(private?readonly?appService:?AppService){}

          但是在代碼里并有沒有看到實(shí)例化這個(gè) AppService 的地方。這里其實(shí)是把創(chuàng)建這個(gè)實(shí)例對(duì)象的工作交給了 nest 框架,而不是 AppController 自己來創(chuàng)建這個(gè)對(duì)象,這就是所謂的控制反轉(zhuǎn)。而把創(chuàng)建好的 AppService 實(shí)例對(duì)象作為 AppController 實(shí)例化時(shí)的參數(shù)傳給構(gòu)造器就是依賴注入了。

          依賴注入的方式

          依賴注入的實(shí)現(xiàn)主要有三種方式

          1. 構(gòu)造器注入:依賴關(guān)系通過 class 構(gòu)造器提供;
          2. setter 注入:用 setter 方法注入依賴項(xiàng);
          3. 接口注入:依賴項(xiàng)提供一個(gè)注入方法,該方法將把依賴項(xiàng)注入到傳遞給它的任何客戶端中。客戶端必須實(shí)現(xiàn)一個(gè)接口,該接口的 setter 方法接收依賴;在 nest 中采用了第一種方式——構(gòu)造器注入。

          優(yōu)點(diǎn)

          那么 nestjs 框架用了依賴注入控制反轉(zhuǎn)有什么好處呢?

          其實(shí) DIIoC 是實(shí)現(xiàn)依賴倒置原則的具體手段。依賴倒置原則是設(shè)計(jì)模式五大原則(SOLID)中的第五項(xiàng)原則,也許上面這個(gè) AppController 的例子還看不出 DIP 有什么用,因?yàn)?DIP 也不是今天的重點(diǎn),這里就不多贅述了,但是通過上面的例子我們至少能體會(huì)到以下兩個(gè)優(yōu)點(diǎn):

          1. 減少樣板代碼,不需要再在業(yè)務(wù)代碼中寫大量實(shí)例化對(duì)象的代碼了;
          2. 可讀性和可維護(hù)性更高了,松耦合,高內(nèi)聚,符合單一職責(zé)原則,一個(gè)類應(yīng)該專注于履行其職責(zé),而不是創(chuàng)建履行這些職責(zé)所需的對(duì)象。

          元數(shù)據(jù)反射

          我們都知道 ts 中的類型信息是在運(yùn)行時(shí)是不存在的,那運(yùn)行時(shí)是如何根據(jù)參數(shù)的類型注入對(duì)應(yīng)實(shí)例的呢?

          答案就是:元數(shù)據(jù)反射

          先說反射,反射就是在運(yùn)行時(shí)動(dòng)態(tài)獲取一個(gè)對(duì)象的一切信息:方法/屬性等等,特點(diǎn)在于動(dòng)態(tài)類型反推導(dǎo)。不管是在 ts 中還是在其他類型語言中,反射的本質(zhì)在于元數(shù)據(jù)。在 TypeScript 中,反射的原理是通過編譯階段對(duì)對(duì)象注入元數(shù)據(jù)信息,在運(yùn)行階段讀取注入的元數(shù)據(jù),從而得到對(duì)象信息。

          元數(shù)據(jù)反射(Reflect Metadata) 是 ES7 的一個(gè)提案,它主要用來在聲明的時(shí)候添加和讀取元數(shù)據(jù)。TypeScript 在 1.5+ 的版本已經(jīng)支持它。要在 ts 中啟用元數(shù)據(jù)反射相關(guān)功能需要:

          • npm i reflect-metadata --save
          • tsconfig.json 里配置 emitDecoratorMetadata 選項(xiàng)為true

          定義元數(shù)據(jù)

          Reflect.defineMetadata(metadataKey, data, target)

          可以定義一個(gè)類的元數(shù)據(jù);

          獲取元數(shù)據(jù)

          Reflect.getMetadata(metadataKey, target)Reflect.getMetadata(metadataKey, instance, methodName)

          可以獲取類或者方法上定義的元數(shù)據(jù)。

          內(nèi)置元數(shù)據(jù)

          TypeScript 結(jié)合自身語言的特點(diǎn),為使用了裝飾器的代碼聲明注入了 3 組元數(shù)據(jù):

          • design:type:成員類型
          • design:paramtypes:成員所有參數(shù)類型
          • design:returntype:成員返回類型

          示例一:元數(shù)據(jù)的定義與獲取

          import?'reflect-metadata';

          class?A?{
          ??sayHi()?{
          ????console.log('hi');
          ??}
          }

          class?B?{
          ??sayHello()?{
          ????console.log('hello');
          ??}
          }

          function?Module(metadata)?{
          ??const?propsKeys?=?Object.keys(metadata);
          ??return?(target)?=>?{
          ????for?(const?property?in?metadata)?{
          ??????if?(metadata.hasOwnProperty(property))?{
          ????????Reflect.defineMetadata(property,?metadata[property],?target);
          ??????}
          ????}
          ??};
          }

          @Module({
          ??controllers:?[B],
          ??providers:?[A],
          })
          class?C?{}

          const?providers?=?Reflect.getMetadata('providers',?C);
          const?controllers?=?Reflect.getMetadata('controllers',?C);

          console.log(providers,?controllers);?//?[?[class?A]?]?[?[class?B]?]



          (new?(providers[0])).sayHi();?//?'hi'

          在這個(gè)例子里,我們定義了一個(gè)名為 Module 的裝飾器,這個(gè)裝飾器的主要作用就是往裝飾的類上添加一些元數(shù)據(jù)。然后用裝飾器裝飾 C 類。我們就可以獲取到這個(gè)參數(shù)中的信息了;

          示例二:依賴注入的簡(jiǎn)單實(shí)現(xiàn)

          import?'reflect-metadata';

          type?Constructorany>?=?new?(...args:?any[])?=>?T;

          const?Test?=?():?ClassDecorator?=>?(target)?=>?{};

          class?OtherService?{
          ??a?=?1;
          }

          @Test()
          class?TestService?{
          ??constructor(public?readonly?otherService:?OtherService)?{}

          ??testMethod()?{
          ????console.log(this.otherService.a);
          ??}
          }

          const?Factory?=?(target:?Constructor):?T?=>?{
          ??//?獲取所有注入的服務(wù)
          ??const?providers?=?Reflect.getMetadata('design:paramtypes',?target);?//?[OtherService]
          ??const?args?=?providers.map((provider:?Constructor)?=>?new?provider());
          ??return?new?target(...args);
          };

          Factory(TestService).testMethod();?//?1

          這里例子就是依賴注入簡(jiǎn)單的示例,這里 Test 裝飾器雖然什么都沒做,但是如上所說,只要使用了裝飾器,ts 就會(huì)默認(rèn)給類或?qū)?yīng)方法添加design:paramtypes的元數(shù)據(jù),這樣就可以通過Reflect.getMetadata('design:paramtypes', target)拿到類型信息了。

          nest 中的實(shí)現(xiàn)

          下面來看 nest 框架內(nèi)部是怎么來實(shí)現(xiàn)的

          執(zhí)行邏輯

          在入口文件 main.ts 中有這樣一行代碼

          const?app?=?await?NestFactory.create(AppModule);

          在源碼 nest/packages/core/nest-application.ts 找到 NestFactory.create 方法,這里用注釋解釋說明了與依賴注入相關(guān)的幾處代碼(下同)。

          public?async?createextends?INestApplication?=?INestApplication>(
          ????module:?any,
          ????serverOrOptions?:?AbstractHttpAdapter?|?NestApplicationOptions,
          ????options?:?NestApplicationOptions,
          ??):?Promise?{
          ????const?[httpServer,?appOptions]?=?this.isHttpServer(serverOrOptions)
          ????????[serverOrOptions,?options]
          ??????:?[this.createHttpAdapter(),?serverOrOptions];

          ????const?applicationConfig?=?new?ApplicationConfig();
          ????//?1.?實(shí)例化?IoC?容器,這個(gè)容器就是用來存放所有對(duì)象的地方
          ????const?container?=?new?NestContainer(applicationConfig);?
          ????this.setAbortOnError(serverOrOptions,?options);
          ????this.registerLoggerConfiguration(appOptions);

          ????//?2.?執(zhí)行初始化邏輯,是依賴注入的核心邏輯所在
          ????await?this.initialize(module,?container,?applicationConfig,?httpServer);?
          ????
          ????//?3.?實(shí)例化?NestApplication?類
          ????const?instance?=?new?NestApplication(?????
          ??????container,
          ??????httpServer,
          ??????applicationConfig,
          ??????appOptions,
          ????);
          ????const?target?=?this.createNestInstance(instance);
          ????//?4.?生成一個(gè)?Proxy?代理對(duì)象,將對(duì)?NestApplication?實(shí)例上部分屬性的訪問代理到?httpServer,在?nest?中httpServer?默認(rèn)就是?express?實(shí)例對(duì)象,所以默認(rèn)情況下,express?的中間件都是可以使用的
          ????return?this.createAdapterProxy(target,?httpServer);?
          ??}

          IoC 容器

          在目錄 nest/packages/core/injector/container.ts,找到了 NestContainer 類,里面有很多成員屬性和方法,可以看到其中的私有屬性 modules 是一個(gè) ModulesContainer 實(shí)例對(duì)象,而 ModulesContainer 類是 Map 類的一個(gè)子類。


          export?class?NestContainer?{???
          ??...
          ??private?readonly?modules?=?new?ModulesContainer();
          ??...
          }
          export?class?ModulesContainer?extends?Map<string,?Module>?{
          ???private?readonly?_applicationId?=?uuid();
          ??
          ???get?applicationId():?string?{
          ?????return?this._applicationId;
          ???}
          }

          依賴注入過程

          先來看 this.initialize 方法:

          ??private?async?initialize(
          ????module:?any,
          ????container:?NestContainer,
          ????config?=?new?ApplicationConfig(),
          ????httpServer:?HttpServer?=?null,
          ??)?{
          ??//?1.?實(shí)例加載器
          ????const?instanceLoader?=?new?InstanceLoader(container);??
          ????const?metadataScanner?=?new?MetadataScanner();?????
          ????//?2.?依賴掃描器
          ????const?dependenciesScanner?=?new?DependenciesScanner(???
          ??????container,
          ??????metadataScanner,
          ??????config,
          ????);
          ????container.setHttpAdapter(httpServer);

          ????const?teardown?=?this.abortOnError?===?false???rethrow?:?undefined;
          ????await?httpServer?.init();
          ????try?{
          ??????this.logger.log(MESSAGES.APPLICATION_START);

          ??????await?ExceptionsZone.asyncRun(
          ????????async?()?=>?{
          ??????????//?3.?掃描依賴
          ??????????await?dependenciesScanner.scan(module);?
          ??????????//?4.?生成依賴的實(shí)例
          ??????????await?instanceLoader.createInstancesOfDependencies();?
          ??????????dependenciesScanner.applyApplicationProviders();
          ????????},
          ????????teardown,
          ????????this.autoFlushLogs,
          ??????);
          ????}?catch?(e)?{
          ??????this.handleInitializationError(e);
          ????}
          ??}
          • new InstanceLoader()實(shí)例化 InstanceLoader 類,并把剛才的 IoC 容器作為參數(shù)傳入,這個(gè)類是專門用來生成需要注入的實(shí)例對(duì)象的;
          • 實(shí)例化 MetadataScanner 類和 DependenciesScanner 類,MetadataScanner 類是一個(gè)用來獲取元數(shù)據(jù)的工具類,而 DependenciesScanner 類是用來掃描出所有 modules 中的依賴項(xiàng)的。上面的 app.module.ts 中 Module 裝飾器的參數(shù)中傳入了controllersproviders等其他選項(xiàng),這個(gè) Module 裝飾器的作用就是標(biāo)明 AppModule 類的一些依賴項(xiàng);
          ???@Module({
          ?????imports:?[],
          ?????controllers:?[AppController],
          ?????providers:?[AppService],
          ???})
          ???export?class?AppModule?{}
          • 調(diào)用依賴掃描器的 scan 方法,掃描依賴;
          ???public?async?scan(module:?Type)?{
          ?????await?this.registerCoreModule();?//?1.?把一些內(nèi)建module添加到IoC容器中
          ?????await?this.scanForModules(module);?//?2.?把傳入的module添加到IoC容器中
          ?????await?this.scanModulesForDependencies();?//?3.?掃描當(dāng)前IoC容器中所有module的依賴
          ?????this.calculateModulesDistance();
          ???
          ?????this.addScopedEnhancersMetadata();
          ?????this.container.bindGlobalScope();
          ???}

          這里所說的 module 可以理解為是模塊,但并不是 es6 語言中的模塊化的 module,而是 app.module.ts 中定義的類, 而 nest 內(nèi)部也有一個(gè)內(nèi)建的 Module 類,框架會(huì)根據(jù) app.module.ts 中定義的 module 類去實(shí)例化一個(gè)內(nèi)建的 Moudle 類。下面 addModule 方法是把 module 添加到 IoC 容器的方法,可以看到,這里針對(duì)每個(gè) module 會(huì)生成一個(gè) token,然后實(shí)例化內(nèi)建的 Module 類,并放到容器的 modules 屬性上,token 作為 Map 結(jié)構(gòu)的 key,Module 實(shí)例作為值。

          ?public?async?addModule(
          ???metatype:?Type<any>?|?DynamicModule?|?Promise,
          ???scope:?Type<any>[],
          ?):?Promiseundefined>?{
          ???//?In?DependenciesScanner#scanForModules?we?already?check?for?undefined?or?invalid?modules
          ???//?We?still?need?to?catch?the?edge-case?of?`forwardRef(()?=>?undefined)`
          ???if?(!metatype)?{
          ?????throw?new?UndefinedForwardRefException(scope);
          ???}
          ???const?{?type,?dynamicMetadata,?token?}?=?await?this.moduleCompiler.compile(
          ?????metatype,
          ???);?//?生成token
          ???if?(this.modules.has(token))?{
          ?????return?this.modules.get(token);
          ???}
          ???const?moduleRef?=?new?Module(type,?this);?//?實(shí)例化內(nèi)建Module類
          ???moduleRef.token?=?token;
          ???this.modules.set(token,?moduleRef);?//?添加在modules上

          ???await?this.addDynamicMetadata(
          ?????token,
          ?????dynamicMetadata,
          ?????[].concat(scope,?type),
          ???);

          ???if?(this.isGlobalModule(type,?dynamicMetadata))?{
          ?????this.addGlobalModule(moduleRef);
          ???}
          ???return?moduleRef;
          ?}
          • scanModulesForDependencies方法會(huì)找到容器中每個(gè) module 上的一些元數(shù)據(jù),把對(duì)應(yīng)的元數(shù)據(jù)分別添加到剛才添加到容器中的 module 上面,這些元數(shù)據(jù)就是根據(jù)上面提到的 Module ?裝飾器的參數(shù)生成的;
          • instanceLoader.createInstancesOfDependencies()
          private?async?createInstances(modules:?Map<string,?Module>)?{
          ?????await?Promise.all(
          ???????[...modules.values()].map(async?moduleRef?=>?{
          ?????????await?this.createInstancesOfProviders(moduleRef);
          ?????????await?this.createInstancesOfInjectables(moduleRef);
          ?????????await?this.createInstancesOfControllers(moduleRef);
          ?
          ?????????const?{?name?}?=?moduleRef.metatype;
          ?????????this.isModuleWhitelisted(name)?&&
          ???????????this.logger.log(MODULE_INIT_MESSAGE`${name}`);
          ???????}),
          ?????);
          ??}

          遍歷 modules 然后生成 provider、Injectable、controller 的實(shí)例。生成實(shí)例的順序上也是有講究的,controller 是最后生成的。在生成實(shí)例的過程中,nest 還會(huì)先去找到構(gòu)造器中的依賴項(xiàng):

          const?dependencies?=?isNil(inject)?
          ????this.reflectConstructorParams(wrapper.metatype?as?Type<any>)?
          ??:?inject;
          reflectConstructorParams(type:?Type):?any[]?{
          ?????const?paramtypes?=?Reflect.getMetadata(PARAMTYPES_METADATA,?type)?||?[];
          ?????const?selfParams?=?this.reflectSelfParams(type);
          ?
          ?????selfParams.forEach(({?index,?param?})?=>?(paramtypes[index]?=?param));
          ?????return?paramtypes;
          ?}
          • 上面代碼中的的常量 PARAMTYPES_METADATA 就是 ts 中內(nèi)置的;metadataKey design:paramtypes,獲取到構(gòu)造參數(shù)類型信息;然后就可以先實(shí)例化依賴項(xiàng);
          async?instantiateClass(instances,?wrapper,?targetMetatype,?contextId?=?constants_2.STATIC_CONTEXT,?inquirer)?{
          ?????????const?{?metatype,?inject?}?=?wrapper;
          ?????????const?inquirerId?=?this.getInquirerId(inquirer);
          ?????????const?instanceHost?=?targetMetatype.getInstanceByContextId(contextId,?inquirerId);
          ?????????const?isInContext?=?wrapper.isStatic(contextId,?inquirer)?||
          ?????????????wrapper.isInRequestScope(contextId,?inquirer)?||
          ?????????????wrapper.isLazyTransient(contextId,?inquirer)?||
          ?????????????wrapper.isExplicitlyRequested(contextId,?inquirer);
          ?????????if?(shared_utils_1.isNil(inject)?&&?isInContext)?{
          ?????????????instanceHost.instance?=?wrapper.forwardRef
          ???????????????????Object.assign(instanceHost.instance,?new?metatype(...instances))
          ?????????????????:?new?metatype(...instances);
          ?????????}
          ?????????else?if?(isInContext)?{
          ?????????????const?factoryReturnValue?=?targetMetatype.metatype(...instances);
          ?????????????instanceHost.instance?=?await?factoryReturnValue;
          ?????????}
          ?????????instanceHost.isResolved?=?true;
          ?????????return?instanceHost.instance;
          ?}
          • 依賴項(xiàng)全部實(shí)例化后再調(diào)用 instantiateClass 方法,依賴項(xiàng)作為第一個(gè)參數(shù) instances 傳入。這里的 new metatype(...instances) 把依賴項(xiàng)的實(shí)例作為參數(shù)全部傳入。

          執(zhí)行流程圖

          NestFactory.create 方法的執(zhí)行邏輯大概如下

          總結(jié)

          1. 元數(shù)據(jù)反射是實(shí)現(xiàn)依賴注入的基礎(chǔ);
          2. 總結(jié)依賴注入的過程,nest 主要做了三件事情
            1. 知道哪些類需要哪些對(duì)象
            2. 創(chuàng)建對(duì)象
            3. 并提供所有這些對(duì)象

          參考

          • nestjs官方文檔 (https://docs.nestjs.com)
          • 深入理解Typescript——Reflect Metadata (https://jkchao.github.io/typescript-book-chinese/tips/metadata.html#%E5%9F%BA%E7%A1%80)
          • Dependency injection in Angular (https://angular.io/guide/dependency-injection)
          • 裝飾器 (https://www.typescriptlang.org/docs/handbook/decorators.html)
          • 從 JavaScript 到 TypeScript 4 - 裝飾器和反射 (https://segmentfault.com/a/1190000011520817)
          • 反射的本質(zhì)——元數(shù)據(jù) (https://developer.aliyun.com/article/382120)
          • 《大話設(shè)計(jì)模式》——程杰


          往期推薦


          給力!快速了解Rust 模塊使用方式
          45 個(gè) Git 經(jīng)典操作場(chǎng)景,專治不會(huì)合代碼
          第一次拿全年年終獎(jiǎng)的前端女程序員的2021

          最后


          • 歡迎加我微信,拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

          • 歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個(gè)專業(yè)的技術(shù)人...

          點(diǎn)個(gè)在看支持我吧
          瀏覽 38
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青青青青青操 | 综合热久久 | 无码免费视频 | 99肏屄| 亚洲第一网站在线观看 |