繁体   English   中英

使用自定义设置器或自定义注释修改 POJO class 字段(在 Spring Boot 中)

[英]Modify POJO class fields with custom setter or custom annotation (in Spring Boot)

给定 Spring Boot 中的一个 POJO,它有几十个String类型的字段,由 Jackson 反序列化。出于演示目的,以下示例仅包含三个字段:

@NoArgsConstructor
public class SomeRequest {

  @JsonProperty("field_1")
  private String field1;

  @JsonProperty("field_2")
  private String field2;

  @JsonProperty("field_3")
  private String field3;
}

我正在寻找一种覆盖 setter 方法的方法,但仅限于某些字段,即我想避免为每个受影响的字段重复以下代码。 这对于少数几个领域是可行的,但对于少数领域来说会变得乏味。

public setField2(String field2) {
  this.field2 = field2 + "?";
}

我的想法是像这样在字段上放置注释:

@NoArgsConstructor
public class SomeRequest {

  // ...

  @JsonProperty("field_2")
  @AppendQuestionMark
  private String field2;

  // ...
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AppendQuestionMark {
}

但是我缺少有关如何“实施” AppendQuestionMark注释的信息,该注释将覆盖字段的 setter 方法。

还是我想的太复杂了?

如果那是你要问的,你不能改变setter方法的主体。 但是您可以创建一个方法,将 object(即SomeRequest )作为输入并检查哪些字段具有您的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();;
}

然后我创建了另一个 class 'AppendStrImpl` 来处理实现。 我使用了以下代码 -

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();
    }
}

这是我的 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
}

然后我从 main 方法中调用了这个方法 -

POJO pojo = new POJO("a", "b", "c", "d");
AppendStrImpl appendStrImpl = new AppendStrImpl();
try {
    appendStrImpl.changeFields(pojo);
} catch (Exception e) {
    e.printStackTrace();
}

现在您可以使用硬编码进行此调用,或者您也可以根据需要使用@Aspect

github 链接在这里

您可以在您感兴趣的字符串字段上使用已经存在的JsonDeserialize注释,而不是创建一个将问号附加到您的 pojo 中的一个通用字符串字段的新注释:

@Data
@NoArgsConstructor
public class SomeRequest {

  @JsonProperty("field_1")
  private String field1;

  @JsonProperty("field_2")
  //here the custom deserializer appends the question mark character
  @JsonDeserialize(using = StringAppendQuestionMarkDeserializer.class)
  private String field2; 
  
}

在您的 spring 引导项目中,您可以使用JsonComponent注释注册自定义解串器,如下所示:

@JsonComponent
public class StringAppendQuestionMarkDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        return node.asText() + "?";
    }

}

使用自定义解串器的 spring 引导测试示例:

@JsonTest
class CorespringApplicationTests {

    @Test
    void testDeserialize() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        SomeRequest request = mapper.readValue("{\"field_1\":\"value1\",\"field_2\":\"value2\"}", SomeRequest.class);
        System.out.println(request); //<-- SomeRequest(field1=value1, field2=value2?)

    }

}

像下面这样的东西应该可以解决问题:

@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]) + "?"});
    }

}

当然,建议检查是否只有一个参数存在,并且它实际上是一个String 或者您也可以将切入点定义为仅应用于以set开头的方法。 但是代码的本质就在那里。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM