如何基于JsonProperty注解值调用Java类字段的Getter/Setter方法?并实现免遍历字段的优化
刚好碰到过类似的场景,我来给你梳理几个可行的方案,完全满足你不想遍历字段的需求,同时也会讲下Jackson和Gson的实现方式:
一、不遍历字段的反射实现方案
Java原生反射API并没有直接提供「根据注解值查找字段/方法」的接口,但我们可以通过提前缓存映射关系的方式,避免每次调用都遍历所有字段。具体思路是在类加载时一次性遍历字段,把JsonProperty的值和对应的字段/方法存入Map,后续调用直接从Map中取值即可。
方案1:缓存字段直接操作
import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonProperty; public class AnotherClass { // 静态缓存:JsonProperty值 -> 对应的字段,只初始化一次 private static final Map<String, Field> JSON_FIELD_MAP = new HashMap<>(); static { // 类加载时遍历一次MyClass的字段,建立映射 for (Field field : MyClass.class.getDeclaredFields()) { JsonProperty annotation = field.getAnnotation(JsonProperty.class); if (annotation != null) { JSON_FIELD_MAP.put(annotation.value(), field); field.setAccessible(true); // 提前设置可访问,避免重复操作 } } } public void myMethod(MyClass myClass, String jsonProperty, String newId) throws IllegalAccessException { Field targetField = JSON_FIELD_MAP.get(jsonProperty); if (targetField != null) { // 设置值 targetField.set(myClass, newId); // 获取值 String currentId = (String) targetField.get(myClass); } } }
方案2:缓存Getter/Setter方法(更符合JavaBean规范)
如果你想严格调用setId()和getId()而不是直接操作字段,可以缓存对应的方法:
import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonProperty; public class AnotherClass { private static final Map<String, Method> JSON_SETTER_MAP = new HashMap<>(); private static final Map<String, Method> JSON_GETTER_MAP = new HashMap<>(); static { // 遍历方法,关联对应的JsonProperty for (Method method : MyClass.class.getDeclaredMethods()) { if (method.getName().startsWith("set")) { // 从setter方法名推断字段名(如setId -> id) String fieldName = method.getName().substring(3); fieldName = Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1); try { Field field = MyClass.class.getDeclaredField(fieldName); JsonProperty annotation = field.getAnnotation(JsonProperty.class); if (annotation != null) { JSON_SETTER_MAP.put(annotation.value(), method); } } catch (NoSuchFieldException e) { continue; // 忽略不符合规范的setter } } else if (method.getName().startsWith("get")) { String fieldName = method.getName().substring(3); fieldName = Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1); try { Field field = MyClass.class.getDeclaredField(fieldName); JsonProperty annotation = field.getAnnotation(JsonProperty.class); if (annotation != null) { JSON_GETTER_MAP.put(annotation.value(), method); } } catch (NoSuchFieldException e) { continue; } } } } public void myMethod(MyClass myClass, String jsonProperty, String newId) throws Exception { // 调用setId() Method setter = JSON_SETTER_MAP.get(jsonProperty); if (setter != null) { setter.invoke(myClass, newId); } // 调用getId() Method getter = JSON_GETTER_MAP.get(jsonProperty); if (getter != null) { String currentId = (String) getter.invoke(myClass); } } }
二、用Jackson ObjectMapper实现(推荐)
Jackson本身就已经处理了JsonProperty的映射逻辑,完全不用自己写反射代码,直接利用它的API就能完成需求:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; public class AnotherClass { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public void myMethod(MyClass myClass, String jsonProperty, String newId) throws Exception { // 将对象转为Jackson的ObjectNode(自动识别JsonProperty) ObjectNode node = OBJECT_MAPPER.valueToTree(myClass); // 通过JsonProperty的值设置属性 node.put(jsonProperty, newId); // 把修改同步回原对象 OBJECT_MAPPER.updateValue(myClass, node); // 获取属性值(直接从对象或node中取都可以) String currentId = myClass.getId(); // 或者 String currentId = node.get(jsonProperty).asText(); } }
这个方案最省心,Jackson会自动处理注解、类型转换等细节,而且代码简洁。
三、用Google Gson实现
Gson也可以完成类似操作,不过它默认会创建新对象,若要修改原实例需要额外处理:
import com.google.gson.Gson; import com.google.gson.JsonObject; public class AnotherClass { private static final Gson GSON = new Gson(); public void myMethod(MyClass myClass, String jsonProperty, String newId) { // 转为JsonObject(自动识别JsonProperty) JsonObject jsonObject = GSON.toJsonTree(myClass).getAsJsonObject(); // 设置属性值 jsonObject.addProperty(jsonProperty, newId); // 转换为新的MyClass实例 MyClass updatedInstance = GSON.fromJson(jsonObject, MyClass.class); // 把新实例的属性同步回原对象(如果需要修改原实例) myClass.setId(updatedInstance.getId()); // 获取属性值 String currentId = jsonObject.get(jsonProperty).getAsString(); } }
四、补充说明
为什么Java原生反射没有直接根据注解值查找元素的方法?因为注解是附加在字段/方法上的元数据,JVM并没有为注解值建立反向索引,所以必须遍历一次来构建映射。但通过静态缓存的方式,我们只需要遍历一次,后续调用都可以直接取缓存,性能上完全没问题。
内容的提问来源于stack exchange,提问作者mang4521
相关产品推荐
相关产品推荐

