JavaScript AOP编程 🎯
今天,让我们深入了解JavaScript中的AOP(面向切面编程)。AOP是一种强大的编程范式,它能帮助我们更好地处理横切关注点,提高代码的模块化程度。
AOP基础概念 🌟
💡 小知识:AOP(Aspect-Oriented Programming)是一种通过分离横切关注点来增加程序的模块化的编程范式。横切关注点是指那些影响多个类或模块的功能,如日志记录、性能统计、安全控制等。
AOP的核心概念 📊
// 1. 切面(Aspect)
class Aspect {
// 前置通知
before(target, methodName, advice) {
const original = target[methodName];
target[methodName] = function(...args) {
advice.apply(this, args);
return original.apply(this, args);
};
}
// 后置通知
after(target, methodName, advice) {
const original = target[methodName];
target[methodName] = function(...args) {
const result = original.apply(this, args);
advice.call(this, result);
return result;
};
}
// 环绕通知
around(target, methodName, advice) {
const original = target[methodName];
target[methodName] = function(...args) {
return advice.call(this, {
target: this,
method: original,
args: args
});
};
}
// 异常通知
afterThrowing(target, methodName, advice) {
const original = target[methodName];
target[methodName] = function(...args) {
try {
return original.apply(this, args);
} catch (error) {
advice.call(this, error);
throw error;
}
};
}
// 最终通知
afterFinally(target, methodName, advice) {
const original = target[methodName];
target[methodName] = function(...args) {
try {
return original.apply(this, args);
} finally {
advice.call(this);
}
};
}
}
// 2. 切点(Pointcut)
class Pointcut {
constructor(pattern) {
this.pattern = pattern;
}
// 方法匹配
matchMethod(methodName) {
if (typeof this.pattern === 'string') {
return methodName === this.pattern;
}
if (this.pattern instanceof RegExp) {
return this.pattern.test(methodName);
}
return false;
}
// 类匹配
matchClass(target) {
return target.constructor.name.match(this.pattern);
}
}
// 3. 通知(Advice)
class Advice {
// 创建日志通知
static createLoggingAdvice() {
return {
before: (target, method, args) => {
console.log(`Calling ${method} with args:`, args);
},
after: (result) => {
console.log('Method returned:', result);
},
error: (error) => {
console.error('Method threw error:', error);
}
};
}
// 创建性能监控通知
static createPerformanceAdvice() {
return {
before: () => {
this.startTime = performance.now();
},
after: (result) => {
const endTime = performance.now();
console.log(`Method took ${endTime - this.startTime}ms`);
return result;
}
};
}
}
AOP实现方式 🔧
// 1. 装饰器实现
function aopDecorators() {
// 日志装饰器
function log(target, key, descriptor) {
const original = descriptor.value;
descriptor.value = function(...args) {
console.log(`Calling ${key} with args:`, args);
const result = original.apply(this, args);
console.log(`${key} returned:`, result);
return result;
};
return descriptor;
}
// 性能监控装饰器
function measure(target, key, descriptor) {
const original = descriptor.value;
descriptor.value = function(...args) {
const start = performance.now();
const result = original.apply(this, args);
const end = performance.now();
console.log(`${key} took ${end - start}ms`);
return result;
};
return descriptor;
}
// 重试装饰器
function retry(times = 3, delay = 1000) {
return function(target, key, descriptor) {
const original = descriptor.value;
descriptor.value = async function(...args) {
for (let i = 0; i < times; i++) {
try {
return await original.apply(this, args);
} catch (error) {
if (i === times - 1) throw error;
await new Promise(resolve =>
setTimeout(resolve, delay)
);
console.log(`Retrying ${key} (${i + 1}/${times})`);
}
}
};
return descriptor;
};
}
}
// 2. 代理实现
class AopProxy {
static create(target, advice) {
return new Proxy(target, {
get(target, property) {
const value = target[property];
if (typeof value !== 'function') return value;
return function(...args) {
if (advice.before) {
advice.before(target, property, args);
}
let result;
try {
result = value.apply(target, args);
if (advice.after) {
advice.after(result);
}
} catch (error) {
if (advice.error) {
advice.error(error);
}
throw error;
}
return result;
};
}
});
}
}
// 3. 中间件实现
class AopMiddleware {
constructor() {
this.middlewares = [];
}
use(middleware) {
this.middlewares.push(middleware);
return this;
}
execute(target, methodName, args) {
const stack = this.middlewares.map(middleware => ({
before: middleware.before?.bind(middleware),
after: middleware.after?.bind(middleware),
error: middleware.error?.bind(middleware)
}));
const executeStack = async (index = 0) => {
if (index >= stack.length) {
return target[methodName].apply(target, args);
}
const middleware = stack[index];
try {
if (middleware.before) {
await middleware.before(target, methodName, args);
}
const result = await executeStack(index + 1);
if (middleware.after) {
await middleware.after(result);
}
return result;
} catch (error) {
if (middleware.error) {
await middleware.error(error);
}
throw error;
}
};
return executeStack();
}
}
AOP实际应用 💼
让我们看看AOP在实际开发中的一些应用场景:
// 1. 日志系统
class LoggingSystem {
static createLogger() {
return {
before(target, method, args) {
console.log(`[${new Date().toISOString()}] Calling ${
target.constructor.name
}.${method}`);
console.log('Arguments:', args);
},
after(result) {
console.log('Result:', result);
},
error(error) {
console.error('Error:', error);
// 可以添加错误上报逻辑
}
};
}
static applyLogging(target, methods) {
const logger = this.createLogger();
const aspect = new Aspect();
methods.forEach(method => {
aspect.before(target, method, logger.before);
aspect.after(target, method, logger.after);
aspect.afterThrowing(target, method, logger.error);
});
}
}
// 2. 性能监控
class PerformanceMonitor {
constructor() {
this.metrics = new Map();
}
createAdvice(metricName) {
return {
before: () => {
this.startTime = performance.now();
},
after: (result) => {
const duration = performance.now() - this.startTime;
if (!this.metrics.has(metricName)) {
this.metrics.set(metricName, []);
}
this.metrics.get(metricName).push(duration);
return result;
}
};
}
getMetrics(metricName) {
const measurements = this.metrics.get(metricName) || [];
if (measurements.length === 0) return null;
return {
average: measurements.reduce((a, b) => a + b, 0) / measurements.length,
min: Math.min(...measurements),
max: Math.max(...measurements),
count: measurements.length
};
}
}
// 3. 缓存系统
class CacheAspect {
constructor() {
this.cache = new Map();
}
createAdvice(options = {}) {
const { ttl = 60000, keyGenerator = (...args) => JSON.stringify(args) } = options;
return {
before: (target, method, args) => {
const key = `${target.constructor.name}.${method}:${
keyGenerator(...args)
}`;
if (this.cache.has(key)) {
const { value, timestamp } = this.cache.get(key);
if (Date.now() - timestamp < ttl) {
return { cached: true, value };
}
this.cache.delete(key);
}
return { cached: false, key };
},
after: (result, context) => {
if (!context.cached) {
this.cache.set(context.key, {
value: result,
timestamp: Date.now()
});
}
return context.cached ? context.value : result;
}
};
}
}
性能优化 ⚡
AOP实现中的性能优化技巧:
// 1. 切面优化
class OptimizedAspect {
// 使用WeakMap存储原始方法
#originalMethods = new WeakMap();
// 优化的方法包装
wrapMethod(target, methodName, advice) {
if (!this.#originalMethods.has(target)) {
this.#originalMethods.set(target, new Map());
}
const methodMap = this.#originalMethods.get(target);
if (!methodMap.has(methodName)) {
methodMap.set(methodName, target[methodName]);
}
const original = methodMap.get(methodName);
return function(...args) {
return advice.call(this, {
target: this,
method: original,
args: args
});
};
}
// 恢复原始方法
unwrapMethod(target, methodName) {
const methodMap = this.#originalMethods.get(target);
if (methodMap && methodMap.has(methodName)) {
target[methodName] = methodMap.get(methodName);
methodMap.delete(methodName);
}
}
}
// 2. 缓存优化
class CacheOptimizer {
static createCachedAspect(options = {}) {
const {
maxSize = 1000,
ttl = 60000,
keyGenerator = (...args) => JSON.stringify(args)
} = options;
const cache = new Map();
const timestamps = new Map();
return {
before(target, method, args) {
const key = keyGenerator(target, method, args);
const now = Date.now();
// 清理过期缓存
if (timestamps.has(key)) {
const timestamp = timestamps.get(key);
if (now - timestamp > ttl) {
cache.delete(key);
timestamps.delete(key);
}
}
// 缓存命中
if (cache.has(key)) {
return { cached: true, value: cache.get(key) };
}
// 缓存大小控制
if (cache.size >= maxSize) {
const oldestKey = timestamps.keys().next().value;
cache.delete(oldestKey);
timestamps.delete(oldestKey);
}
return { cached: false, key };
},
after(result, context) {
if (!context.cached) {
cache.set(context.key, result);
timestamps.set(context.key, Date.now());
}
return context.cached ? context.value : result;
}
};
}
}
// 3. 执行优化
class ExecutionOptimizer {
// 批量处理优化
static batchProcess(aspect, batchSize = 100) {
const queue = [];
let processing = false;
return {
async before(target, method, args) {
queue.push({ target, method, args });
if (queue.length >= batchSize && !processing) {
processing = true;
await this.processBatch();
processing = false;
}
},
async processBatch() {
const batch = queue.splice(0, batchSize);
return Promise.all(
batch.map(({ target, method, args }) =>
aspect.execute(target, method, args)
)
);
}
};
}
}
最佳实践建议 💡
- AOP使用场景
// 1. 横切关注点处理
function crossCuttingConcerns() {
// 认证切面
const authAspect = {
before(target, method, args) {
if (!isAuthenticated()) {
throw new Error('未认证');
}
}
};
// 权限切面
const authorizationAspect = {
before(target, method, args) {
if (!hasPermission(method)) {
throw new Error('无权限');
}
}
};
// 事务切面
const transactionAspect = {
async around(joinPoint) {
const transaction = await beginTransaction();
try {
const result = await joinPoint.method.apply(
joinPoint.target,
joinPoint.args
);
await transaction.commit();
return result;
} catch (error) {
await transaction.rollback();
throw error;
}
}
};
}
// 2. 代码复用
function codeReuse() {
// 重试机制
const retryAspect = {
async around(joinPoint) {
const maxRetries = 3;
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await joinPoint.method.apply(
joinPoint.target,
joinPoint.args
);
} catch (error) {
lastError = error;
if (i < maxRetries - 1) {
await new Promise(resolve =>
setTimeout(resolve, 1000 * Math.pow(2, i))
);
}
}
}
throw lastError;
}
};
// 缓存机制
const cacheAspect = {
before(target, method, args) {
const key = `${target.constructor.name}.${method}:${
JSON.stringify(args)
}`;
const cached = cache.get(key);
if (cached) return { cached: true, value: cached };
return { cached: false, key };
},
after(result, context) {
if (!context.cached) {
cache.set(context.key, result);
}
return context.cached ? context.value : result;
}
};
}
// 3. 调试和监控
function debuggingAndMonitoring() {
// 调试切面
const debugAspect = {
before(target, method, args) {
console.log(`[DEBUG] Entering ${method}`);
console.log('Arguments:', args);
},
after(result) {
console.log('Result:', result);
console.log('[DEBUG] Exiting method');
},
error(error) {
console.error('[DEBUG] Error:', error);
}
};
// 监控切面
const monitoringAspect = {
before() {
this.startTime = performance.now();
},
after(result) {
const duration = performance.now() - this.startTime;
metrics.record('method.duration', duration);
return result;
},
error(error) {
metrics.increment('method.errors');
throw error;
}
};
}
结语 📝
AOP是一种强大的编程范式,它能帮助我们更好地组织和管理代码。我们学习了:
- AOP的核心概念和实现方式
- 不同类型的切面和通知
- 实际应用场景
- 性能优化技巧
- 最佳实践和注意事项
💡 学习建议:在使用AOP时,要注意平衡其带来的好处和复杂性。过度使用AOP可能会使代码难以理解和调试。建议从简单的日志、性能监控等场景开始,逐步扩展到更复杂的应用。
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻