I have the following classes :
@Getter
@Setter
public class MyClassA extends MyClassB{
private String name;
private String firstName;
}
@Getter
@Setter
public class MyClassB {
@MyCustomAnnotation
private String age;
}
// MyCustomAnnotation class
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface MyCustomAnnotation{
}
Then I can easily retrieve the property value by using org.apache.commons.beanutils.PropertyUtils for example.
MyClassA bean = new MyClassA("foo", "bar", 25);
String value = PropertyUtils.getProperty(bean, "age");
System.out.println(value);
My question is, Is there any efficient way (like when retrieving the value above) to check if @MyCustomAnnotation is present on a field or its getter, and no matter if the field is on the current class or the sub-class, without using complex reflection ? I've done some research on org.springframework.util.Reflectionutils, org.springframework.beans.BeanUtils and org.springframework.bean.BeanWrapper but they seems to not have any solution on my problem.
Note : To avoid confusion, I know that it is possible using reflection. Question is like PropertyUtils/Beanwrapper for retrieving value, is there library to get the annotation. I just don't want to have org.apache.commons.beanutils.PropertyUtils library to get the value and have some java reflection to get the annotation.
Thanks for your help and sorry for my english, it's not my mother language
CodePudding user response:
Your solution should look something like this:
UPDATE:
I created a utility class where you pass the target object, and the rest of the parameters from the previous example. The only differences are that
- You have to set fields (and methods) accessible if they are not public.
- If the declared field is not in the target's class, then you got to check the parent. Potentially, you might have to make this function recursive to go all the way up the hierarch tree, but for this simple example of one parent class, it works fine.
- The parent class fields should not be private. They should be at least
protected
because private fields are not accessible by the child class.
With that, given the following classes with annotated field and with this custom annotation...
public class MyClassB {
@MyCustomAnnotation
protected String age;
}
public class MyClassA extends MyClassB {
private String name;
private String firstName;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface MyCustomAnnotation{
}
public class AnnotationCheck {
public static boolean isFieldAnnotationPresent(Object target, Class annotation, String name) throws SecurityException, NoSuchFieldException {
Field field = null;
boolean isPresent = false;
try {
field = target.getClass().getDeclaredField(name);
field.setAccessible(true);
} catch (NoSuchFieldException nsfe) {
Class parent = target.getClass().getSuperclass();
if(parent == null) {
throw nsfe;
} else {
field = parent.getDeclaredField(name);
field.setAccessible(true);
}
}
return field.isAnnotationPresent(annotation);
}
}
If I run this test:
public class TestAnnotation {
public static void main(String[] args) throws NoSuchFieldException {
MyClassA target = new MyClassA();
System.out.println(AnnotationCheck.isFieldAnnotationPresent(target, MyCustomAnnotation.class, "age"));
System.out.println(AnnotationCheck.isFieldAnnotationPresent(target, MyCustomAnnotation.class, "firstName"));
}
}
I get the following output:
true
false
CodePudding user response:
Ok, I finally find the solution by using org.springframework.beans.BeanWrapper that support nested properties. I can retrieve the value and annotation no matter if it's annotated on the field or on the getter
So here is my final solution :
String fieldName = "age";
MyClassA bean = new MyClassA("foo", "bar", 25);
BeanWrapper wrapper = new BeanWrapperImpl(bean);
// to property value
Object value = wrapper.getPropertyValue(fieldName);
// to get annotation
wrapper.getPropertyTypeDescriptor(fieldName).getAnnotation(MyCustomAnnotation.class)