Given a POJO in Spring Boot with several dozen fields of type String
which is deserialized by Jackson. For demonstration purposes the following example only contains three fields:
@NoArgsConstructor
public class SomeRequest {
@JsonProperty("field_1")
private String field1;
@JsonProperty("field_2")
private String field2;
@JsonProperty("field_3")
private String field3;
}
I'm looking for a way to override the setter method but only for certain fields, i.e. I'd like to avoid repeating the below code for every affected field. This is doable for a handful number of fields but gets tedious for more than a handful.
public setField2(String field2) {
this.field2 = field2 "?";
}
My idea was to place an annotation on the field like this:
@NoArgsConstructor
public class SomeRequest {
// ...
@JsonProperty("field_2")
@AppendQuestionMark
private String field2;
// ...
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AppendQuestionMark {
}
But I'm lacking information on how to "implement" the AppendQuestionMark
annotation which would override the field's setter method.
Or am I thinking way too complicated?
CodePudding user response:
Something like the following should do the trick:
@Aspect
@Component
public class AppendQuestionMarkAspect {
@Around("@annotation(AppendQuestionMark)")
public Object appendQuestionMark(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] arguments = joinPoint.getArgs();
return joinPoint.proceed(new Object[] {((String) arguments[0]) "?"});
}
}
Of course, it would be advisable to check that only one argument exists and that it is, in fact, a String
. Or you can also define the pointcut as to be applied only to methods starting with set
. But the essence of the code is there.
CodePudding user response:
You can't change the setter
method's body if that's what you are asking. But you can create a method that will take an object (i.e. SomeRequest
) as input and check which fields have your Annotation
and change the values for those fields as you want.
For example, I created an annotation AppendStr
.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AppendStr {
public String str();;
}
Then I created another class 'AppendStrImpl` that will handle the implementation. I used the following code -
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class AppendStrImpl {
public void changeFields(Object object) throws Exception {
Class<?> clazz = object.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
if (field.isAnnotationPresent(AppendStr.class)) {
// get the getter method name from the field name
String fieldName = field.getName();
String getterMethodName =
"get"
fieldName.substring(0, 1).toUpperCase()
fieldName.substring(1);
Method getterMethod = clazz.getMethod(getterMethodName);
String returnValue = (String) getterMethod.invoke(object);
String setterMethodName = getterMethodName.substring(0, 1).replace("g", "s")
getterMethodName.substring(1);
Method setterMethod = clazz.getMethod(setterMethodName, String.class);
setterMethod.invoke(object, returnValue getAppendingString(field));
System.out.println((String) getterMethod.invoke(object));
}
}
}
private String getAppendingString(Field field) {
return field.getAnnotation(AppendStr.class)
.str();
}
}
And this is my POJO class -
public class POJO {
@AppendStr(str = "?")
private String filed1;
@AppendStr(str = "!")
private String filed2;
private String filed3;
@AppendStr(str = " ")
private String filed4;
// ... getters and setters
}
Then I called this method from the main method -
POJO pojo = new POJO("a", "b", "c", "d");
AppendStrImpl appendStrImpl = new AppendStrImpl();
try {
appendStrImpl.changeFields(pojo);
} catch (Exception e) {
e.printStackTrace();
}
Now you can make this call with hard coding or you can use @Aspect
too if you want.
The github link is here.