addSingleton用处
向我们的每一个woker进程中的全局应用对象app上挂载一个单例对象,方便在应用中使用。
使用addSingleton的一些插件
- egg-mysql
- egg-redis
- egg-oss
- ...
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一致,区别就是调用create
是createInstanceAsync
方法支持异步,加了个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;
}