gpt4 book ai didi

java - 使用 Java Reflection API 在运行时修改字段的声明注释

转载 作者:行者123 更新时间:2023-11-30 02:37:29 25 4
gpt4 key购买 nike

来自this answer通过创建和安装新的内部 AnnotationData 对象,可以在运行时向 Java 类添加注释。我很好奇是否可以使用Field。看起来 Field 处理注释的方式与 Class 处理注释的方式非常不同。

我已经能够使用以下类成功将注释添加到 Field 类的 declaredAnnotations 字段:

public class FieldRuntimeAnnotations {

private static final Field DECLARED_ANNOTATIONS_FIELD;
private static final Method DECLARED_ANNOTATIONS_METHOD;

static {
try {
DECLARED_ANNOTATIONS_METHOD = Field.class.getDeclaredMethod("declaredAnnotations");
DECLARED_ANNOTATIONS_METHOD.setAccessible(true);

DECLARED_ANNOTATIONS_FIELD = Field.class.getDeclaredField("declaredAnnotations");
DECLARED_ANNOTATIONS_FIELD.setAccessible(true);

} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}

// Public access method
public static <T extends Annotation> void putAnnotationToField(Field f, Class<T> annotationClass, Map<String, Object> valuesMap) {
T annotationValues = TypeRuntimeAnnotations.annotationForMap(annotationClass, valuesMap);

try {

Object annotationData = DECLARED_ANNOTATIONS_METHOD.invoke(f);

// Get declared annotations
Map<Class<? extends Annotation>, Annotation> declaredAnnotations =
(Map<Class<? extends Annotation>, Annotation>) DECLARED_ANNOTATIONS_FIELD.get(f);

// Essentially copy our original annotations to a new LinkedHashMap
Map<Class<? extends Annotation>, Annotation> newDeclaredAnnotations = new LinkedHashMap<>(declaredAnnotations);

newDeclaredAnnotations.put(annotationClass, annotationValues);

DECLARED_ANNOTATIONS_FIELD.set(f, newDeclaredAnnotations);

} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
}

但是,字段的声明类不会使用正确的 ReflectionData 进行更新。所以本质上我需要用它的声明类来“安装”新的字段信息,但我很难弄清楚如何操作。

为了更清楚地说明我的要求,我的测试中的第三个断言失败了:

public class RuntimeAnnotationsTest {

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface TestAnnotation {}

public static class TestEntity {
private String test;
}

@Test
public void testPutAnnotationToField() throws NoSuchFieldException {

// Confirm class does not have annotation
TestAnnotation annotation = TestEntity.class.getDeclaredField("test").getAnnotation(TestAnnotation.class);
Assert.assertNull(annotation);

Field f = TestEntity.class.getDeclaredField("test");
f.setAccessible(true);

FieldRuntimeAnnotations.putAnnotationToField(f, TestAnnotation.class, new HashMap<>());

// Make sure field annotation gets set
Assert.assertNotNull(f.getAnnotation(TestAnnotation.class));

// Make sure the class that contains that field is also updated -- THIS FAILS
Assert.assertNotNull(TestEntity.class.getDeclaredField("test").getAnnotation(TestAnnotation.class));
}

}

我知道我想要实现的目标相当荒谬,但我很享受这个练习:D ...有什么想法吗?

最佳答案

使用 TestEntity.class.getDeclaredField("test"),您可以获得 TestEntity.class 私有(private)内部字段的副本,但您需要原始字段。我扩展了您的测试用例,从 Class.class 中的私有(private)方法“privateGetDeclaredFields”获取原始私有(private)字段。

import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.junit.Assert;
import org.junit.Test;

public class FieldRuntimeAnnotationsTest {
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface TestAnnotation {}

public static class TestEntity {
private String test;
}

@Test
public void testPutAnnotationToField() throws NoSuchFieldException {
// Confirm class does not have annotation
TestAnnotation annotation = TestEntity.class.getDeclaredField("test").getAnnotation(TestAnnotation.class);
Assert.assertNull(annotation);

// This field is a copy of the internal one
Field f = TestEntity.class.getDeclaredField("test");
f.setAccessible(true);

FieldRuntimeAnnotations.putAnnotationToField(f, TestAnnotation.class, new HashMap<>());

// Make sure field annotation gets set
Assert.assertNotNull(f.getAnnotation(TestAnnotation.class));

// Make sure the class that contains that field is not updated -- THE FIELD IS A COPY
Assert.assertNull(TestEntity.class.getDeclaredField("test").getAnnotation(TestAnnotation.class));

// Repeat the process with the internal field
Field f2 = getDeclaredField(TestEntity.class, "test");
f2.setAccessible(true);

FieldRuntimeAnnotations.putAnnotationToField(f2, TestAnnotation.class, new HashMap<>());

// Make sure field annotation gets set
Assert.assertNotNull(f2.getAnnotation(TestAnnotation.class));

// Make sure the class that contains that field is also updated -- THE FIELD IS THE ORIGINAL ONE
Assert.assertNotNull(TestEntity.class.getDeclaredField("test").getAnnotation(TestAnnotation.class));
}

public Field getDeclaredField(Class<?> clazz, String name) {
if (name == null || name.isEmpty()) {
return null;
}
Field[] fields = getDeclaredFields(clazz);
Field field = null;
for (Field f : fields) {
if (name.equals(f.getName())) {
field = f;
}
}
return field;
}

public Field[] getDeclaredFields(Class<?> clazz) {
if (clazz == null) {
return new Field[0];
}
Method privateGetDeclaredFieldsMethod = null;
Object value = null;
try {
privateGetDeclaredFieldsMethod = Class.class.getDeclaredMethod("privateGetDeclaredFields", boolean.class);
privateGetDeclaredFieldsMethod.setAccessible(true);
value = privateGetDeclaredFieldsMethod.invoke(clazz, Boolean.FALSE);
}
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Assert.fail("Error for " + clazz + ", exception=" + e.getMessage());
}
Field[] fields = value == null ? new Field[0] : (Field[])value;
return fields;
}
}

关于java - 使用 Java Reflection API 在运行时修改字段的声明注释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42680491/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com