【问题标题】:Modify POJO class fields with custom setter or custom annotation (in Spring Boot)使用自定义 setter 或自定义注解修改 POJO 类字段(在 Spring Boot 中)
【发布时间】:2021-11-18 15:41:56
【问题描述】:

在 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 方法。

还是我想的太复杂了?

【问题讨论】:

  • 如果在 getter 方法中完成,它是否也可以满足您的用例的目的?
  • 是的,如果它使事情变得更容易,那也可以达到目的????
  • 当我看到问题时我的想法是创建一个自定义反序列化程序并在您需要的字段上使用@JsonDeserialize 注释而不是自定义注释,您可以在您的项目中使用它们吗?

标签: java spring-boot jackson


【解决方案1】:

以下内容应该可以解决问题:

@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 开头的方法。但是代码的精髓就在那里。

【讨论】:

【解决方案2】:

如果这是您要求的,您不能更改 settermethod 的主体。但是您可以创建一个将对象(即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();;
}

然后我创建了另一个类“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 课程 -

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链接是here

【讨论】:

    【解决方案3】:

    您可以在您感兴趣的字符串字段上使用已经存在的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 Boot 项目中,您可以使用 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 Boot 测试示例:

    @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?)
    
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-26
      • 2022-01-04
      • 1970-01-01
      • 2019-03-15
      • 2020-11-04
      • 1970-01-01
      • 2013-02-24
      • 1970-01-01
      相关资源
      最近更新 更多