egg 插件-addSingleton

2,144 阅读1分钟

addSingleton用处

向我们的每一个woker进程中的全局应用对象app上挂载一个单例对象,方便在应用中使用。

使用addSingleton的一些插件

  1. egg-mysql
  2. egg-redis
  3. egg-oss
  4. ...

addSingleton源码

egg.js

addSingleton(name, create) {
    const options = {};
    options.name = name;
    options.create = create;
    options.app = this;
    // 初始化Singleton
    const singleton = new Singleton(options);
    // 执行init
    const initPromise = singleton.init();
    if (initPromise) {
      this.beforeStart(async () => {
        await initPromise;
      });
    }
}

lib/core/singleton.js

class Singleton {
  constructor(options = {}) {
    this.clients = new Map();
    this.app = options.app;
    this.name = options.name;
    this.create = options.create;
    // 获取插件的配置
    this.options = options.app.config[this.name] || {};
  }
  // 判断函数是否是async函数 
  // 调用
  init() {
    return is.asyncFunction(this.create) ? this.initAsync() : this.initSync();
  }
  // 同步执行方式
  initSync() {
    // 插件的配置
    const options = this.options;
     // 针对配置了client的方式
    if (options.client) {
      // 创建实例
      const client = this.createInstance(options.client, options.name);
      // 设置成app上的一个属性
      // app也是我们的worker进程中的单例,this.app or this.ctx.app
      // 对应的插件引用方式 this.app[pluginName].xxx
      this.app[this.name] = client;
      this._extendDynamicMethods(client);
      return;
    }
    // 如果有多个client配置,比如需要连接多个数据库 可以使用this.app['xxx'].get(id)获取
    if (options.clients) {
      Object.keys(options.clients).forEach(id => {
        const client = this.createInstance(options.clients[id], id);
        this.clients.set(id, client);
      });
      // 将当前的singleton实例设置成对应的属性
      this.app[this.name] = this;
      return;
    }
    // 没有配置client的话就直接挂载当前的singleton实例
    this.app[this.name] = this;
  }
  
}

先进入到createInstance方法,之后再看this.initAsync

createInstance

// 创建实例
createInstance(config, clientName) {
    // 合并了default配置项和client配置
    config = Object.assign({}, this.options.default, config);
    // 执行我们声明的create方法
    // config就是我们插件的配置项
    // this.app 就是 当前的worker进程上的app对象
    // 所以我们要在create方法中返回我们想要挂载到app对象上的一些实例,比如 new Mysql() new Redis()的实例
    return this.create(config, this.app, clientName);
}

再回到initAsync

基本上和initsync一致,区别就是调用createcreateInstanceAsync方法支持异步,加了个await

async initAsync() {
    if (options.client) {
      const client = await this.createInstanceAsync(options.client, options.name);
      this.app[this.name] = client;
      this._extendDynamicMethods(client);
      return;
    }

    // multi clent, use app[name].getInstance(id)
    if (options.clients) {
      await Promise.all(Object.keys(options.clients).map(id => {
        return this.createInstanceAsync(options.clients[id], id)
          .then(client => this.clients.set(id, client));
      }));
      this.app[this.name] = this;
      return;
    }

    // no config.clients and config.client
    this.app[this.name] = this;
}