在运行时使用Java反射API修改字段的声明注释
通过创建和安装新的内部在运行时使用Java反射API修改字段的声明注释,java,reflection,Java,Reflection,通过创建和安装新的内部AnnotationData对象,可以在运行时向Java类添加注释。我很好奇是否可以设置一个字段。似乎字段处理注释的方式与类处理注释的方式截然不同 我已经能够使用以下类成功地向字段类的declaredAnnotations字段添加注释: public class FieldRuntimeAnnotations { private static final Field DECLARED_ANNOTATIONS_FIELD; private static final
AnnotationData
对象,可以在运行时向Java类添加注释。我很好奇是否可以设置一个字段
。似乎字段
处理注释的方式与类
处理注释的方式截然不同
我已经能够使用以下类成功地向字段
类的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);
}
}
}
我明白我想要达到的目标是相当荒谬的,但我很享受这个练习:D。。。有什么想法吗?使用TestEntity.class.getDeclaredField(“test”)可以获得TestEntity.class的私有内部字段的副本,但需要原始字段。 我扩展了您的测试用例,从Class.Class中的私有方法“privateGetDeclaredFields”获取原始私有字段
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;
}
}
import java.lang.annotation.RetentionPolicy;
导入java.lang.annotation.ElementType;
导入java.lang.annotation.Retention;
导入java.lang.annotation.Target;
导入java.lang.reflect.Field;
导入java.lang.reflect.InvocationTargetException;
导入java.lang.reflect.Method;
导入java.util.HashMap;
导入java.util.Map;
导入org.junit.Assert;
导入org.junit.Test;
公共类FieldRuntimeAnnotationsTest{
@保留(RetentionPolicy.RUNTIME)
@目标({ElementType.TYPE,ElementType.FIELD})
public@interface TestAnnotation{}
公共静态类测试{
私有字符串测试;
}
@试验
public void testPutAnnotationToField()抛出NoSuchFieldException{
//确认类没有注释
TestAnnotation=TestEntity.class.getDeclaredField(“测试”).getAnnotation(TestAnnotation.class);
Assert.assertNull(注释);
//此字段是内部字段的副本
字段f=TestEntity.class.getDeclaredField(“测试”);
f、 setAccessible(true);
FieldRuntimeAnnotations.putAnnotationToField(f,TestAnnotations.class,new HashMap());
//确保设置了字段注释
Assert.assertNotNull(f.getAnnotation(TestAnnotation.class));
//确保包含该字段的类未更新--该字段是副本
Assert.assertNull(TestEntity.class.getDeclaredField(“test”).getAnnotation(TestAnnotation.class));
//对内部字段重复该过程
字段f2=getDeclaredField(TestEntity.class,“测试”);
f2.可访问设置(真);
FieldRuntimeAnnotations.putAnnotationToField(f2,TestAnnotation.class,new HashMap());
//确保设置了字段注释
Assert.assertNotNull(f2.getAnnotation(TestAnnotation.class));
//确保包含该字段的类也已更新——该字段是原始字段
Assert.assertNotNull(TestEntity.class.getDeclaredField(“test”).getAnnotation(TestAnnotation.class));
}
公共字段getDeclaredField(类clazz,字符串名称){
if(name==null | | name.isEmpty()){
返回null;
}
字段[]字段=getDeclaredFields(clazz);
字段=空;
用于(字段f:字段){
if(name.equals(f.getName())){
字段=f;
}
}
返回字段;
}
公共字段[]getDeclaredFields(类clazz){
if(clazz==null){
返回新字段[0];
}
方法privateGetDeclaredFieldsMethod=null;
对象值=空;
试一试{
privateGetDeclaredFieldsMethod=Class.Class.getDeclaredMethod(“privateGetDeclaredFields”,boolean.Class);
privateGetDeclaredFieldsMethod.setAccessible(true);
value=privateGetDeclaredFieldsMethod.invoke(clazz,Boolean.FALSE);
}
捕获(NoSuchMethodException | IllegalacessException | InvocationTargetException e){
Assert.fail(“错误为“+clazz+”,异常为“+e.getMessage()”);
}
字段[]字段=value==null?新字段[0]:(字段[])值;
返回字段;
}
}
非常有魅力!我错过了调用Class.Class
方法privateGetDeclaredFields
的关键部分,该方法使用带有布尔参数false
的字段声明类。非常感谢。
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;
}
}