Home > OS >  Modify POJO class fields with custom setter or custom annotation (in Spring Boot)
Modify POJO class fields with custom setter or custom annotation (in Spring Boot)

Time:09-26

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 settermethod'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.

  • Related