You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 01:47:47