如何从方法装饰器访问类元数据

Posted

技术标签:

【中文标题】如何从方法装饰器访问类元数据【英文标题】:How to access class metadata from method decorator 【发布时间】:2018-06-28 18:52:33 【问题描述】:

我有两个装饰器。一个类装饰器和一个方法装饰器。 类装饰器定义了我想在方法装饰器中访问的元数据。

类装饰器:

function ClassDecorator(topic?: string): ClassDecorator 
    return (target) => 
        Reflect.defineMetadata('topic', topic, target);
        // I've also tried target.prototype instead of target
        return target;
    ;

方法装饰器:

interface methodDecoratorOptions 
    cmd: string


function MethodDecorator(options: decoratorOptions) 
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) 
        // HERE IS MY PROBLEM
        console.log('metaData is: ', Reflect.getMetadata('topic', target));
    

这是我的类定义:

@ClassDecorator('auth')
export class LoginClass 

    @MethodDecorator(
        cmd: 'login'
    )
    myMethod() 
        console.log('METHOD CALLED');
    

问题:

MethodDecorator 的以下行返回metaData is: undefined。为什么未定义?

console.log('metaData is: ', Reflect.getMetadata('topic', target));

问题:

如何从 MethodDecorator 访问 ClassDecorator 定义的元数据?

【问题讨论】:

【参考方案1】:

问题在于装饰器的执行顺序。首先执行方法装饰器,然后执行类装饰器。如果您考虑一下,这是有道理的,类装饰器需要完整的类来操作,而创建类涉及创建方法并首先调用它们的装饰器。

一个简单的解决方法是方法装饰器注册一个回调,然后在设置主题后由类装饰器调用:

function ClassDecorator(topic?: string): ClassDecorator 
    return (target) => 
        Reflect.defineMetadata('topic', topic, target.prototype);
        let topicFns: Array<() => void> = Reflect.getMetadata("topicCallbacks", target.prototype);
        if (topicFns) 
            topicFns.forEach(fn => fn());
        
        return target;
    ;


interface methodDecoratorOptions 
    cmd: string


function MethodDecorator(options: methodDecoratorOptions) 
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) 
        let topicFns: Array<() => void> = Reflect.getMetadata("topicCallbacks", target);
        if (!topicFns) 
            Reflect.defineMetadata("topicCallbacks", topicFns = [], target);
        
        topicFns.push(() => 
            console.log('metaData is: ', Reflect.getMetadata('topic', target));
        );
    


@ClassDecorator('auth')
class LoginClass 

    @MethodDecorator(
        cmd: 'login'
    )
    myMethod() 
        console.log('METHOD CALLED');
    

【讨论】:

但老实说,我不喜欢这种解决方法。对我来说似乎不太可读。希望能找到更好的解决方案。 我不知道另一种解决方法,它确实使代码更难阅读.. 但是装饰器再次成为您的基础架构的一部分,您将编写一次并使用多次,以及使用的装饰器没有改变,我认为这是更重要的部分 您可以取消标记答案并等待更好的解决方法,我不介意:)。但是我已经看到并使用了类似的解决方法来解决这个问题,但还没有找到更好的解决方法,只是这个主题的变体(例如使用静态类字段而不是 Reflect` api) 我不是故意冒犯你的。我非常感谢我现在确实理解了这个问题。我不明白为什么开发人员选择了装饰器执行的顺序。也许他们有这样做的原因,但对我来说这很不方便。 @tmuecksch 没有冒犯,真的:)。我确信某处有一个标准可以强制执行此操作。而且我相信他们考虑了另一种方式,但是对于大多数用例来说,这种顺序更有意义,不幸的是不适合你

以上是关于如何从方法装饰器访问类元数据的主要内容,如果未能解决你的问题,请参考以下文章

java 类元数据是不是在堆上?

自制方法装饰器擦除所有元数据,我该如何解决?

使用 proguard / R8 删除数据类元数据

类元数据Class Metadata

Angular装饰器介绍

LayaBox---TypeScript---装饰器