给定一个输入对象,我需要使用给定的注释查找其中的所有字段,并用一些值对其进行修改。
此注释也可以存在于给定输入对象内部的对象中。
public class A {
private B b;
@MyAnnotation
private String s;
}
public class B {
@MyAnnotation
private String a;
private String b;
}
在这种情况下,输出应为s和a字段,我需要使用一些String对其进行修改。我从apache找到了fieldUtils,它为给定的类提供了注释,我想我可以添加一个简单的DFS搜索来查找具有给定注释的所有字段。使用反射设置值时出现问题,我需要将对象指定为field.set()
的一部分。我不确定在设置a的值时应使用哪个对象,以及如何以一般方式获取它(以防在本示例中需要从B
检索A
的实例)< / p>
答案 0 :(得分:1)
要递归地修改对象中带有给定注释的所有字段,我们必须先确定所有字段。
Class.getFields()
将仅返回“ 可访问的公共字段”。这不符合要求。因此,我们必须使用Class.getDeclaredFields()
。反过来,这仅返回字段“ 由类声明的”。为了获取所有字段,我们必须向上遍历目标对象的类型层次结构,并针对其声明的字段的每种类型进行处理。
对于每个字段,我们都必须区分一些情况:
Field
添加了指定的注释。然后必须将给定值应用于该字段。为了设置该值,必须(使)该字段可访问且非最终字段。Field
未使用指定的注释进行注释。然后有三种情况可以区分:
String
字段。String
字段。要检查是否在字段上设置了注释,我们使用Field.getAnnotation(myAnnotationClass)
,其中myAnnotationClass
声明为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;
}
答案 1 :(得分:0)
您可以使用Java Reflection API来检查您的类的字段:
获取每个字段的声明的注释;
检查您是否有注释。
好吧,感谢Reflection API,这并非难事。
答案 2 :(得分:0)
您可以在必须向下钻取的字段上添加一些标记注释。 Bean验证使用类似的方法。
下面,Bar
嵌套在Foo
中,并用DrillDown
进行注释,以指示必须将其钻取,以查找要更新的字段。
class Foo {
@Updateable String a;
@DrillDown Bar bar;
String c;
}
class Bar {
@Updateable String b;
}
演示它的快速示例代码,
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.annotation.Retention;
class Main {
public static void main(String[] args) throws Exception {
Foo foo = new Foo();
foo.a = "a-old";
foo.c = "non-updateable";
Bar bar = new Bar();
bar.b = "b-old";
foo.bar = bar;
update("new-value", foo);
System.out.println(foo.a);
System.out.println(foo.bar.b);
System.out.println(foo.c);
}
public static void update(String value, Object obj) throws Exception {
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
Updateable updateable = field.getAnnotation(Updateable.class);
if (updateable != null) {
field.set(obj, value);
} else {
DrillDown drillDown = field.getAnnotation(DrillDown.class);
if (drillDown != null) {
update(value, field.get(obj));
}
}
}
}
}
class Foo {
@Updateable String a;
@DrillDown Bar bar;
String c;
}
class Bar {
@Updateable String b;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface DrillDown {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Updateable {
}