简体   繁体   English

在Java 8中更改字段注释的注释值

[英]Change annotation value of field annotation in Java 8

I think title describes the problem. 我认为标题描述了这个问题。 Here's some code: 这是一些代码:

import static org.junit.Assert.assertEquals;
import java.lang.annotation.*;

public class Main {
    public static void main(String[] args) throws Exception {
        assertEquals("foo", Main.class.getDeclaredMethod("myMethod").getAnnotation(Anno.class).param());

        // the magic here -> set to bar

        assertEquals("bar", Main.class.getDeclaredMethod("myMethod").getAnnotation(Anno.class).param());
    }

    @Anno(param = "foo")
    public void myMethod() {}

    @Retention(RetentionPolicy.RUNTIME)
    @interface Anno {
        String param();
    }
}

So far I'm guessing this is not possible. 到目前为止,我猜这是不可能的。 It seems that always when you try to get a method via reflection you only get copies and all values (like annotations) are reread from a deeper java layer. 似乎总是当你试图通过反射获得一个方法时,你只能获得副本,所有的值(如注释)都会从更深层的java层重新读取。 In these copies you could change values but these are gone if you reload. 在这些副本中,您可以更改值,但如果重新加载,这些值就会消失。

Is there something I missed or is it really not possible? 有什么我错过了或者它真的不可能吗?

Annotations are modifiers just like private or synchronized . 注释是修饰符 ,就像privatesynchronized They are part of the invariant static structure of a class and not intended to be modified. 它们是类的不变静态结构的一部分,并不打算进行修改。 You can hack into the Reflection implementation to make a particular method print what you want, but besides the dirtiness of the hack, you simply haven't changed the annotation, you just hacked a data structure of a particular library. 您可以入侵Reflection实现,使特定方法打印出您想要的内容,但除了黑客的肮脏之外,您只是没有更改注释,您只是破解了特定库的数据结构。

There are other Reflection or byte code manipulation libraries which won't use the built-in Reflection API but read the byte code directly (eg via getResource() or via the Instrumentation API). 还有其他反射或字节代码操作库,它们不使用内置的Reflection API,而是直接读取字节代码(例如通过getResource()或通过Instrumentation API)。 These libraries will never notice your manipulation. 这些库永远不会注意到你的操纵。

Note further, since these values are supposed to be constants embedded in the class file, a Reflection implementation could always use lazy fetching plus dropping of the cached values depending on uncontrollable conditions as these values can always be refetched. 进一步注意,由于这些值应该是嵌入在类文件中的常量,因此Reflection实现总是可以使用延迟读取以及根据不可控条件丢弃缓存值,因为始终可以重新获取这些值。 There's also no guaranty that the Reflection implementation uses data structures at all; 也没有保证Reflection实现完全使用数据结构; it could also generate code returning the constant values. 它还可以生成返回常量值的代码。

In other words, if you want to associate mutable data with a method, don't use annotations. 换句话说,如果要将可变数据与方法关联,请不要使用注释。 You could simple use a Map<Method,MutableData> , or, if you just have that one particular method, declare a good old static field which already provides all the features you need and is much easier to handle. 您可以简单地使用Map<Method,MutableData> ,或者,如果您只有一个特定的方法,则声明一个好的旧static字段,它已经提供了您需要的所有功能,并且更容易处理。

I posted a link to do the same task before Java 8. It seems to be possible to do the same in Java 8 (1.8.0_51). 我在Java 8之前发布了一个链接来执行相同的任务。似乎可以在Java 8(1.8.0_51)中执行相同的操作。 The whole test setup 整个测试设置

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

public class Test {
    public static void main(String... args) {
        Test t = new Test();
        t.check();
        Test.change();
        t.check();
        new Test().check();
    }

    public Test() {

    }

    public static void change() {
        try {
            Method m = Test.class.getMethod("myMethod");
            // The map has to be built for the first time
            m.getDeclaredAnnotations();
            Class<?> superclass = m.getClass().getSuperclass();
            Field declaredField = superclass.getDeclaredField("declaredAnnotations");
            declaredField.setAccessible(true);
            @SuppressWarnings("unchecked")
            Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) declaredField
                    .get(m);
            map.put(Anno.class, new Anno() {

                @Override
                public Class<? extends Annotation> annotationType() {
                    return Anno.class;
                }

                @Override
                public String param() {
                    return "new";
                }
            });
        } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | NoSuchFieldException
                | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public void check() {
        try {
            System.out.println(getClass().getMethod("myMethod").getAnnotation(Anno.class).param());
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
    }

    @Anno(param = "test")
    public void myMethod() {
    }

}

@Retention(RetentionPolicy.RUNTIME)
@interface Anno {
    String param();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM