Skip to main content

NodeJS 面向切面编程

一段帮助理解面向切面编程的代码#

  • Package.json
// 摘抄部分重要{  "scripts": {    "dev": "ts-node app.ts"  },  "dependencies": {    "global": "^4.4.0",    "inversify": "^5.0.5",    "reflect-metadata": "^0.1.13",    "ts-node": "^9.1.1",    "typescript": "^4.1.3"  }}
  • Interfaces.ts (个人感觉更像java里面的service 接口层)
interface Student {  learn(): string;}
interface Teacher {  teaching(): string;}
interface Classroom {  study(): string;}
export { Student, Teacher, Classroom };
  • Entities.ts (这个像Java里面的接口实现Impl)
import { Classroom, Student, Teacher } from './interfaces';import { inject, injectable } from 'inversify';import 'reflect-metadata';import TYPES from './types';
@injectable()  // 这里的意思有点像 @Service注解  这里也是起注入功能class XiaoMing implements Student {  public learn() {    return '👊努力学习';  }}
@injectable()  class Zhijia implements Teacher {  public teaching() {    return '教高级前端 🌶';  }}
@injectable()class Yd implements Classroom {  private _xiaoming: Student;  private _zhijia: Teacher;  constructor(@inject(TYPES.Student) xiaoming, @inject(TYPES.Teacher) zhijia) {    this._xiaoming = xiaoming;    this._zhijia = zhijia;  }  public study() {    return this._zhijia.teaching() + this._xiaoming.learn();  }}
export { XiaoMing, Zhijia, Yd };
  • inversify.config.ts (这里把service 跟service实现注入到容器中)
import { Container } from 'inversify';import { XiaoMing, Yd, Zhijia } from './entities';import { Classroom, Student, Teacher } from './interfaces';import Types from './types';
const container = new Container();
container.bind<Student>(Types.Student).to(XiaoMing);container.bind<Teacher>(Types.Teacher).to(Zhijia);container.bind<Classroom>(Types.Classroom).to(Yd);
export default container;
  • app.ts (主程序)
import container from './inversify.config';import TYPES from './types';import { Classroom } from './interfaces';
const classroom = container.get<Classroom>(TYPES.Classroom);console.log(classroom.study());

// 运行 yarn dev 展示如下结果   // yarn run v1.22.4// $ ts-node app.ts// 教高级前端 🌶👊努力学习
  • 其他: tsconfig.json 配置摘要
{    "compilerOptions": {        "target": "es5",        "lib": ["dom","es6"],        "types": ["reflect-metadata"],        "module": "commonjs",        "moduleResolution": "node",        "experimentalDecorators": true,        "emitDecoratorMetadata": true    }}

面向切面编程更进一步#

books demo改写,利用awilix库实现一个简单ioc 的nodejs应用#

  • Package.json
// 部分主要代码{  "scripts": {    "start": "ts-node-dev --respawn --transpile-only app.ts"  },  "dependencies": {    "@types/koa-router": "^7.4.1",    "awilix": "^4.3.1", //  ioc入门的nodejs库    "awilix-koa": "^4.0.0", //  整合koa    "co": "^4.6.0",             //  主要处理异步结果的返回    "koa": "^2.13.0",    "koa-router": "^10.0.0",    "koa-swig": "^2.2.1",    "ts-node-dev": "^1.0.0"  },  "devDependencies": {    "@types/co": "^4.6.2",    "@types/koa": "^2.11.6",    "typescript": "^4.1.2"  },}
  • interface目录
// IData.tsexport interface IData {  item: string;}
// IApi.tsimport { IData } from './IData';
export interface IApi {  getInfo(): Promise<IData>;}
// IKoa.tsimport Koa from 'koa';import render from 'koa-swig';
export interface Context extends Koa.Context {  render: typeof render;}
  • service目录
// ApiService.tsimport { IApi } from '../interface/IApi';import { IData } from '../interface/IData';
class ApiService implements IApi {  // 这里实现interface中定义的IApi接口  getInfo() {    return new Promise<IData>((resolve) => {  //    一个简单的promis返回数据      resolve({        item: '后台数据🌺',      });    });  }}export default ApiService;  //导出
  • Router (其实这里我觉得更像 controller层)
// ApiController.tsimport { GET, route } from 'awilix-koa';import * as Router from 'koa-router';import { IApi } from '../interface/IApi';
@route('/api')class ApiController {  //    创建私有变量  private apiService: IApi;    // 这里把  apiServices 注入到 ApiController 中  constructor({ apiServices }) {    this.apiService = apiServices;  }    @route('/list')  // 路由:访问 http://localhost:3000/api/list  @GET()          // 访问的请求方式: 这里为GET  async actionList(    ctx: Router.IRouterContext,    next: () => Promise<unknown>  ): Promise<any> {    const data = await this.apiService.getInfo();    ctx.body = {      data,    };  }}export default ApiController;


//  IndexController.tsimport { GET, route } from 'awilix-koa';import { Context } from '../interface/IKoa';
@route('/')class ApiController {  @route('/')   // 通过访问根路径,渲染一个 html  @GET()  async actionIndex(ctx: Context): Promise<void> {    ctx.body = await ctx.render('index');  }}export default ApiController;
  • App.ts
import * as Koa from 'koa';//用IOC的方式管理路由import { createContainer, Lifetime } from 'awilix';import { loadControllers, scopePerRequest } from 'awilix-koa';// swig模版包import render from 'koa-swig';import co from 'co';import { join } from 'path';
const app = new Koa();//  初始化容器 IOCconst container = createContainer();//  注入写好的servicecontainer.loadModules([`${__dirname}/services/*.ts`], {  formatName: 'camelCase',  //驼峰式命名  resolverOptions: {    lifetime: Lifetime.SCOPED,  //生命周期   },});//  模版渲染配置app.context.render = co.wrap(  // 这里用co.wrap包裹主要是解决异步结果的返回  render({    root: join(__dirname, 'views'),    autoescape: true,    cache: 'memory',    ext: 'html',    writeBody: false,  }));
//  融合Koaapp.use(scopePerRequest(container));app.use(loadControllers(`${__dirname}/routers/*.ts`));
app.listen(3000, () => {  console.log('TS AOP Node框架启动成功');});
  • typings
//koa-swig.d.ts// 这里主要是针对 koa-swig 的ts支持.d.ts文件declare module 'koa-swig' {  function render<T>(value: T | render.DefaultSettings): any;  namespace render {    interface DefaultSettings {      root: string;      autoescape: boolean;      cache: string;      ext: string;      writeBody: boolean;    }  }  export default render;}
  • views
<!-- 静态文件 --><!DOCTYPE html><html lang="en">  <head>    <meta charset="UTF-8" />    <meta name="viewport" content="width=device-width, initial-scale=1.0" />    <title>TS的项目首页</title>  </head>  <body>    <h1>这里是首页🏮</h1>  </body></html>
  • 最终的的demo目录
.├── app.ts├── interface│   ├── IApi.ts│   ├── IData.ts│   └── IKoa.ts├── package.json├── routers│   ├── ApiController.ts│   └── IndexController.ts├── services│   └── ApiServices.ts├── tsconfig.json├── typings│   └── koa-swig.d.ts├── views│   └── index.html├── yarn-error.log└── yarn.lock

面向切面编程高级: inversify的使用#

使用inversify对books重构,这里的inversely 更贴近面向切面编程#

  • Package.json
{   "scripts": {    "dev": "ts-node app.ts"  },  "dependencies": {    "@types/koa-router": "^7.4.1",    "inversify": "^5.0.5",    "inversify-binding-decorators": "^4.0.0",    "inversify-koa-utils": "^1.0.0",    "reflect-metadata": "^0.1.13",    "ts-node": "^9.1.1",    "typescript": "^4.1.3"  }}
  • interface
// IData.tsexport interface IData {  item: string;}
// IApi.tsimport { IData } from './IData';
export interface IApi {  getInfo(): Promise<IData>;}
// IKoa.tsimport Koa from 'koa';import render from 'koa-swig';
export interface Context extends Koa.Context {  render: typeof render;}
  • contant(常量)
// tags.tsconst TAGS = {  IndexService: Symbol.for('IndexService'),};export default TAGS;
  • controller
import { interfaces, controller, httpGet, TYPE } from 'inversify-koa-utils';import { IApi } from '../interface/IApi';import { provideThrowable } from '../ioc';import { inject } from 'inversify';import TAGS from '../constant/tags';import { IRouterContext } from 'koa-router';
@controller('/')@provideThrowable(TYPE.Controller, 'IndexController')export default class IndexController implements interfaces.Controller {  private indexService: IApi;  constructor(@inject(TAGS.IndexService) indexService) {    this.indexService = indexService;  }  @httpGet('/')  private async index(    ctx: IRouterContext,    next: () => Promise<unknown>  ): Promise<any> {    const data = await this.indexService.getInfo();    ctx.body = {      data,    };  }}
// 1.定义interfaces// 2.定义常量 注入容器// 3.插入到构造函数中 注入过程
  • ioc
// index.tsimport { fluentProvide } from 'inversify-binding-decorators';
let provideThrowable = (identifier, name) => {  return fluentProvide(identifier).whenTargetNamed(name).done();};export { provideThrowable };
//loader.tsimport '../controllers/IndexController';import '../services/IndexService';
  • 可以新增model层
//Data.tsexport namespace Model {  export class User {    name: string;    email: string;  }}// Model.User
  • App.ts
import 'reflect-metadata';import './ioc/loader';import { InversifyKoaServer } from 'inversify-koa-utils';import { Container } from 'inversify';import { buildProviderModule } from 'inversify-binding-decorators';
const container = new Container();container.load(buildProviderModule());
let server = new InversifyKoaServer(container);let app = server.build();
app.listen(3000, () => {  console.log('Inversify Server 启动成功');});
  • 其他:tsconfig.json
{    "compilerOptions": {        "target": "es5",        "lib": ["es6"],        "types": ["reflect-metadata"],        "module": "commonjs",        "moduleResolution": "node",        "experimentalDecorators": true,        "emitDecoratorMetadata": true    }}
  • 最终代码目录
.├── app.ts├── constant│   └── tags.ts├── controllers│   └── IndexController.ts├── interface│   ├── IApi.ts│   ├── IData.ts│   └── IKoa.ts├── ioc│   ├── index.ts│   └── loader.ts├── models│   └── Data.ts├── package.json├── services│   └── IndexService.ts├── tsconfig.json├── yarn-error.log└── yarn.lock