为了账号安全,请及时绑定邮箱和手机立即绑定

递归修改用给定注释注释的对象中的所有字段

递归修改用给定注释注释的对象中的所有字段

ITMISS 2021-08-19 19:15:30
给定一个输入对象,我需要使用给定的注释查找其中的所有字段,并使用某个值对其进行修改。该注释也可以存在于给定输入对象内的对象中。public class A {  private B b;  @MyAnnotation  private String s;}public class B {    @MyAnnotation    private String a;    private String b;}在这种情况下,输出应该是字段 s 和 a,我需要用一些字符串修改相同的内容。我从 apache 中找到了 fieldUtils,它给出了给定类的注释,我想我可以添加一个简单的 DFS 搜索来查找具有给定注释的所有字段。使用反射设置值时出现问题,我需要将对象指定为field.set(). 我不确定在设置 a 的值时应该使用哪个对象以及如何以通用方式获取它(以防我需要在此示例中检索BfromA的实例)
查看完整描述

3 回答

?
慕尼黑8549860

TA贡献1818条经验 获得超11个赞

递归修改用给定注释注释的对象中的所有字段,我们必须首先确定所有字段

Class.getFields()将只返回“可访问的公共字段”。这不符合要求。因此我们必须使用Class.getDeclaredFields(). 这反过来只返回“由类声明”的字段。为了获得所有字段,我们必须向上遍历目标对象的类型层次结构,并为每个类型处理其声明的字段。

对于每个字段,我们必须区分一些情况:

  1. Field 标注为指定的注释。然后必须将给定的值应用于该字段。为了设置该值,该字段必须是(使)可访问且非最终的。

  2. AField 使用指定的注释进行注释。那么要区分三种情况:

    • 该字段具有原始类型。那么它就不是一个可以包含String字段的对象。

    • 该字段具有数组类型。那么它不能包含一个String字段。

    • 否则:该字段具有引用类型。然后必须(使其)可访问,以检查它是否设置了值,并在这种情况下再次递归处理其字段 1) 和 2)。

要检查是否在字段上设置了注释,我们使用Field.getAnnotation(myAnnotationClass)wheremyAnnotationClass声明为Class<MyAnnotation>。但是我们必须注意一些额外的事情:只有在以下情况下
Field.getAnnotation(myAnnotationClass)才会返回字段的注释

  • 它已设置(非常不言自明)

  • MyAnnotation本身用@Retention(RetentionPolicy.RUNTIME).

相应的代码如下所示:

 public static <T extends Annotation> void setValueOnAnnotatedFields(String s, Class<T> annotationClass, Object target) {

    if (target != null) {

      // traversing the type hierarchy upward to process fields declared in classes target's class extends

      Class<?> clazz = target.getClass();

      do {

        setValueOnAnnotatedDeclaredFields(s, annotationClass, target, clazz);

      }

      while ((clazz = clazz.getSuperclass()) != null);

    }

  }


  private static <T extends Annotation> void setValueOnAnnotatedDeclaredFields(String s, Class<T> annotationClass, Object target, Class<?> clazz) {

    Field[] fields = clazz.getDeclaredFields();

    for (Field field : fields) {

      if (field.getAnnotation(annotationClass) != null) {

        set(field, target, s);

      } else if (!field.getType().isPrimitive() && !field.getType().isArray()) {

        setValueOnAnnotatedFields(s, annotationClass, get(field, target));

      }

    }

  }


  protected static void set(Field field, Object target, String value) {

    try {

      makeFiieldAccessibleNotFinal(field, target);

      field.set(target, value);

    }

    catch (Exception e) {

      String message = String.format("Failed to set the value on field %s", target.getClass().getSimpleName() + "." + field.getName());

      throw new IllegalStateException(message, e);

    }

  }


  protected static Object get(Field field, Object target) {

    try {

      makeFiieldAccessibleNotFinal(field, target);

      return field.get(target);

    }

    catch (Exception e) {

      String message = String.format("Failed to get the value on field %s", target.getClass().getSimpleName() + "." + field.getName());

      throw new IllegalStateException(message, e);

    }

  }


  protected static void makeFiieldAccessibleNotFinal(Field field, Object target) {

    try {

      Field modifiersField = Field.class.getDeclaredField("modifiers");

      modifiersField.setAccessible(true);

      modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

    }

    catch (Exception e) {

      String message = String.format("Failed to remove final declaration of field %s", field.getName());

      throw new IllegalStateException(message, e);

    }

    try {

      field.setAccessible(true);

    }

    catch (Exception e) {

      String message = String.format("Failed to make field %s accessible", field.getName());

      throw new IllegalStateException(message, e);

    }

  }

一个例子:


  public static void main(String[] args) {

    A a = new A();

    System.out.println(a); // s: null / b: [null]


    setValueOnAnnotatedFields("1", MyAnnotation.class, a);

    System.out.println(a); // s: 1 / b: [null]


    a.b = new B();

    setValueOnAnnotatedFields("2", MyAnnotation.class, a);

    System.out.println(a); // s: 2 / b: [a: 2 / b: null]

  }

这意味着类A已经toString()实现了:


@Override

public String toString() {

  return "s: " + s + " / b: [" + Optional.ofNullable(b).map(B::toString).orElse("null") + "]";

}

和类B已经toString()实现了这样的:


@Override

public String toString() {

  return "a: " + a + " / b: " + b;

}


查看完整回答
反对 回复 2021-08-19
?
慕森王

TA贡献1777条经验 获得超3个赞

您可以使用 Java 反射 API 来检查您的类的字段:

获取为每个字段声明的注解;

检查您是否有注释..

嗯,多亏了反射 API,这不是一件难事。


查看完整回答
反对 回复 2021-08-19
  • 3 回答
  • 0 关注
  • 266 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信