最近多年前的一个nodejs旧系统,有效能问题,需要对该系统的一些Function添加运行时间的log来判断是哪个Step耗时较多以作针对性优化。
实现该需求当然首选是AOP了,使用装饰器来实现AOP是简单不过的事情,但旧系统根本没有装Babel,不支持装饰器,只能自己动手实现AOP了。
首先添加aop inject function:
// aop.js file
const inject = (decorator, target, methodName) => {
const method = target[methodName];
const descriptor = { ...method, value: method };
decorator(target, methodName, descriptor);
Object.defineProperty(target, methodName, descriptor);
};
module.exports = { inject };
接着添加计时的逻辑代码(与使用装饰器时的代码一致):
// aopBussness.js file
const NS_PER_SEC = 1e9;
const MS_PER_SEC = 1e6;
const testAOP = function (target, methodName, descriptor) {
const method = descriptor.value;
descriptor.value = function (...args) {
const begin = process.hrtime();
let result;
try {
console.info(`befor run, ${Object(target).name}.${methodName} input is:`, JSON.stringify(args));
result = descriptor.apply(this, args);
return result;
} catch (err) {
console.error('error:', err);
throw err;
} finally {
const end = process.hrtime(begin);
const durationMs = (end[0] * NS_PER_SEC + end[1]) / MS_PER_SEC;
console.info(`${Object(target).name}.${methodName} Done. durationMs:${durationMs} `);
}
};
};
module.exports = { testAOP };
最后,将计时代码注入原逻辑代码:
// orgSC.js file
const aop = require('./aop');
const { testAOP } = require('./aopBussness');
// 原代码:
// const tools = {
// fun1(v, s) {
// // ...
// },
//
// fun2(t) {
// // ...
// },
// };
// 改为Class:
class Tools {
static fun1(v,s) {
// ...
}
static fun2(t) {
// ...
}
}
aop.inject(testAOP, Tools, 'fun1'); // 注入
aop.inject(testAOP, Tools, 'fun2'); // 注入
// module.exports = tools;
module.exports = Tools;
这样就可以对tools的fun1和fun2函数添加上耗时计算的逻辑了。
但如果对一个Class的所有Function都需要注入,这样一个个的写就太麻烦了,于是aop.js 可优化为:
// aop.js file
const getMethods = (obj) =>
Object.getOwnPropertyNames(Object.getPrototypeOf(obj)).filter(
(item) => typeof obj[item] === 'function'
);
const inject = (decorator, module, methodName = null) => {
const target = module;
if (typeof methodName === 'undefined' || methodName === null) {
const methods = getMethods(module);
methods.forEach((m) => {
inject(decorator, target, m);
});
} else {
const method = target[methodName];
const descriptor = { ...method, value: method };
decorator(target, methodName, descriptor);
Object.defineProperty(target, methodName, descriptor);
}
};
module.exports = { inject };
而注入时只需要:aop.inject(testAOP,Tools);
就可以针对该Tools Class 全部Funtion注入了。