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

如何使用ByteBuddy为Map包装JavaBean添加自定义Get/Set方法

Got it, let's break this down and get your Map-to-JavaBean wrapper working with ByteBuddy. You’ve already got the builder set up to accept a Map via constructor—great start! Now we just need to wire up the getters and setters to delegate directly to that underlying map.

Using ByteBuddy to Wrap a Map into a JavaBean with Getters/Setters

Here's a step-by-step implementation with concrete code examples, including both fixed-property and dynamic-property approaches:

Core Concept

The goal is to create a dynamic class that:

  1. Stores the input Map in a private field
  2. Generates getter methods that pull values from the map using specific keys
  3. Generates setter methods that push values into the map using those same keys

Full Implementation (Fixed Properties)

Let's say you want to expose keys like username and email as bean properties. Here's how to build the dynamic class:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;

import java.util.Map;

public class MapBeanGenerator {

    // Generate a bean instance from a backing map
    public static <T> T createBean(Map<String, Object> backingMap, Class<T> beanInterface) throws Exception {
        // Start building the dynamic class
        DynamicType.Builder<?> builder = new ByteBuddy()
                .subclass(Object.class)
                .implement(beanInterface) // Optional: Enforce compile-time method contracts
                // Add private field to hold the backing map
                .defineField("backingMap", Map.class, net.bytebuddy.description.modifier.Visibility.PRIVATE)
                // Add constructor that accepts and assigns the map
                .defineConstructor(net.bytebuddy.description.modifier.Visibility.PUBLIC)
                .withParameter(Map.class)
                .intercept(
                    MethodCall.invoke(Object.class.getConstructor())
                    .andThen(FieldAccessor.ofField("backingMap").setsArgumentAt(0))
                );

        // Add getter for "username"
        builder = builder.defineMethod("getUsername", String.class, net.bytebuddy.description.modifier.Visibility.PUBLIC)
                .intercept(MethodDelegation.to(MapGetters.class).withArgument("username"));

        // Add setter for "username"
        builder = builder.defineMethod("setUsername", void.class, net.bytebuddy.description.modifier.Visibility.PUBLIC)
                .withParameter(String.class)
                .intercept(MethodDelegation.to(MapSetters.class).withArgument("username"));

        // Repeat for other properties like "email"
        builder = builder.defineMethod("getEmail", String.class, net.bytebuddy.description.modifier.Visibility.PUBLIC)
                .intercept(MethodDelegation.to(MapGetters.class).withArgument("email"));

        builder = builder.defineMethod("setEmail", void.class, net.bytebuddy.description.modifier.Visibility.PUBLIC)
                .withParameter(String.class)
                .intercept(MethodDelegation.to(MapSetters.class).withArgument("email"));

        // Compile and instantiate the dynamic class
        Class<?> dynamicClass = builder.make().load(MapBeanGenerator.class.getClassLoader()).getLoaded();
        return (T) dynamicClass.getConstructor(Map.class).newInstance(backingMap);
    }

    // Interceptor class for getter logic
    public static class MapGetters {
        public static Object get(Map backingMap, String key) {
            return backingMap.get(key);
        }
    }

    // Interceptor class for setter logic
    public static class MapSetters {
        public static void set(Map backingMap, String key, Object value) {
            backingMap.put(key, value);
        }
    }

    // Optional: Interface defining bean methods for type safety
    public interface UserBean {
        String getUsername();
        void setUsername(String username);
        String getEmail();
        void setEmail(String email);
    }

    // Example usage
    public static void main(String[] args) throws Exception {
        Map<String, Object> userMap = Map.of("username", "jane_doe", "email", "jane@example.com");
        UserBean userBean = createBean(userMap, UserBean.class);

        System.out.println(userBean.getUsername()); // Output: jane_doe
        userBean.setEmail("jane.new@example.com");
        System.out.println(userMap.get("email")); // Output: jane.new@example.com
    }
}

Dynamic Property Generation (No Hardcoded Keys)

If you want to generate getters/setters for all keys in the input Map automatically, replace the fixed method definitions with a loop over the map's entries:

// Inside createBean(), after setting up the constructor
for (Map.Entry<String, Object> entry : backingMap.entrySet()) {
    String key = entry.getKey();
    Class<?> valueType = entry.getValue().getClass();

    // Generate getter name (e.g., "getUsername" for key "username")
    String getterName = "get" + Character.toUpperCase(key.charAt(0)) + key.substring(1);
    builder = builder.defineMethod(getterName, valueType, net.bytebuddy.description.modifier.Visibility.PUBLIC)
            .intercept(MethodDelegation.to(MapGetters.class).withArgument(key));

    // Generate setter name (e.g., "setUsername" for key "username")
    String setterName = "set" + Character.toUpperCase(key.charAt(0)) + key.substring(1);
    builder = builder.defineMethod(setterName, void.class, net.bytebuddy.description.modifier.Visibility.PUBLIC)
            .withParameter(valueType)
            .intercept(MethodDelegation.to(MapSetters.class).withArgument(key));
}

Key Details to Note

  • Field Access: We use FieldAccessor to assign the input Map to the dynamic class's private backingMap field.
  • Method Delegation: MethodDelegation routes getter/setter calls to static interceptor methods, which handle the actual Map operations.
  • Type Safety: Using an interface (like UserBean) ensures your generated bean adheres to a contract, giving you compile-time checks for method names and types.

内容的提问来源于stack exchange,提问作者Naros

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:44:19