[英]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.