MST

星途 面试题库

面试题:TypeScript装饰器与面向切面编程在大型企业级项目中的深度应用与优化

在一个大型企业级微服务架构项目中,使用TypeScript作为主要开发语言。各个微服务之间存在复杂的业务交互和权限控制。请详细说明如何基于TypeScript装饰器进行面向切面编程,以实现统一的权限验证、日志记录、性能监控等功能,同时要考虑到装饰器的性能优化、如何避免重复逻辑以及在分布式环境下的应用。请结合具体的架构设计和代码示例进行阐述。
47.2万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

1. 基于TypeScript装饰器实现面向切面编程

权限验证装饰器

// 权限验证装饰器
function checkPermissions(requiredPermissions: string[]) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        descriptor.value = function (...args: any[]) {
            // 模拟获取当前用户权限
            const userPermissions = ['read', 'write']; 
            const hasPermission = requiredPermissions.some(permission => userPermissions.includes(permission));
            if (!hasPermission) {
                throw new Error('没有足够权限');
            }
            return originalMethod.apply(this, args);
        };
        return descriptor;
    };
}

class UserService {
    @checkPermissions(['read'])
    getUser() {
        return '获取用户信息';
    }
}

日志记录装饰器

// 日志记录装饰器
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`调用方法 ${propertyKey},参数:`, args);
        const result = originalMethod.apply(this, args);
        console.log(`方法 ${propertyKey} 返回结果:`, result);
        return result;
    };
    return descriptor;
}

class ProductService {
    @log
    getProduct() {
        return '获取产品信息';
    }
}

性能监控装饰器

// 性能监控装饰器
function performanceMonitor(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        const start = Date.now();
        const result = originalMethod.apply(this, args);
        const end = Date.now();
        console.log(`方法 ${propertyKey} 执行时间: ${end - start} ms`);
        return result;
    };
    return descriptor;
}

class OrderService {
    @performanceMonitor
    placeOrder() {
        // 模拟业务逻辑
        for (let i = 0; i < 1000000; i++);
        return '订单已提交';
    }
}

2. 装饰器性能优化

  • 缓存计算结果:对于一些计算成本较高的装饰器逻辑,比如权限验证中获取用户权限的操作,可以进行缓存。
const permissionCache: { [key: string]: boolean } = {};
function checkPermissions(requiredPermissions: string[]) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        const cacheKey = requiredPermissions.join(',');
        descriptor.value = function (...args: any[]) {
            let hasPermission;
            if (permissionCache[cacheKey]) {
                hasPermission = permissionCache[cacheKey];
            } else {
                // 模拟获取当前用户权限
                const userPermissions = ['read', 'write']; 
                hasPermission = requiredPermissions.some(permission => userPermissions.includes(permission));
                permissionCache[cacheKey] = hasPermission;
            }
            if (!hasPermission) {
                throw new Error('没有足够权限');
            }
            return originalMethod.apply(this, args);
        };
        return descriptor;
    };
}
  • 减少重复计算:避免在装饰器内部进行不必要的重复计算,将一些固定值提前计算好。

3. 避免重复逻辑

  • 提取公共逻辑:将权限验证、日志记录等装饰器中的公共逻辑提取到独立的函数或模块中。例如,日志记录装饰器中打印日志的格式可以提取出来。
function formatLog(methodName: string, args: any[], result: any) {
    return `调用方法 ${methodName},参数: ${JSON.stringify(args)},返回结果: ${JSON.stringify(result)}`;
}

function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        const result = originalMethod.apply(this, args);
        console.log(formatLog(propertyKey, args, result));
        return result;
    };
    return descriptor;
}
  • 使用继承或组合:对于有相似逻辑的装饰器,可以通过继承或组合的方式复用代码。比如,可以创建一个基础装饰器类,其他装饰器继承自它并扩展功能。

4. 在分布式环境下的应用

  • 集中式权限管理:使用如Redis等分布式缓存来存储权限信息,各个微服务在权限验证时从Redis中获取最新的权限数据。
import redis from'redis';
const client = redis.createClient();

function checkPermissions(requiredPermissions: string[]) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        descriptor.value = async function (...args: any[]) {
            const userPermissions: string[] = await new Promise((resolve, reject) => {
                client.get('userPermissions', (err, reply) => {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(reply? reply.split(',') : []);
                    }
                });
            });
            const hasPermission = requiredPermissions.some(permission => userPermissions.includes(permission));
            if (!hasPermission) {
                throw new Error('没有足够权限');
            }
            return originalMethod.apply(this, args);
        };
        return descriptor;
    };
}
  • 分布式日志聚合:使用如ELK(Elasticsearch, Logstash, Kibana)等工具进行日志聚合和分析。各个微服务将日志发送到Logstash,再由Logstash处理后存储到Elasticsearch,最后通过Kibana进行可视化展示。
  • 性能监控分布式追踪:引入如Jaeger等分布式追踪系统,在性能监控装饰器中添加追踪ID等信息,以便在分布式环境下跟踪整个业务流程的性能。
import tracer from 'jaeger - client';

function performanceMonitor(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = async function (...args: any[]) {
        const span = tracer.startSpan(propertyKey);
        const start = Date.now();
        try {
            const result = await originalMethod.apply(this, args);
            const end = Date.now();
            console.log(`方法 ${propertyKey} 执行时间: ${end - start} ms`);
            span.finish();
            return result;
        } catch (error) {
            span.setTag('error', true);
            span.finish();
            throw error;
        }
    };
    return descriptor;
}

通过以上设计和代码示例,可以基于TypeScript装饰器实现面向切面编程,满足统一的权限验证、日志记录、性能监控等功能,并在性能优化、避免重复逻辑以及分布式环境应用方面有较好的解决方案。