java使用bytebuddy动态生成带泛型的DTO

本文通过多个实例,深入探讨了使用ByteBuddy库动态生成Java类的方法,包括简单类、带注解的类、泛型类、通配符类型类及方法的创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我这人文笔很low,喜欢直接贴代码,大家将就着看

//TODO 文字描述,回头有空再补上

package com.valsong.bytebuddy;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.valsong.dto.User;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.annotation.AnnotationSource;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.Argument;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * byte buddy 创建简单的class
 */
public class ByteBuddyTest {
    /**
     * 构建
     *
     * package net.bytebuddy.renamed.java.lang.Object$com.valsong.dynamicdto;
     *
     * public class User$zulHzyXk {
     *     private String name;
     *     private Integer age;
     *
     *     public void setName(String var1) {
     *         this.name = var1;
     *     }
     *
     *     public String getName() {
     *         return this.name;
     *     }
     *
     *     public void setAge(Integer var1) {
     *         this.age = var1;
     *     }
     *
     *     public Integer getAge() {
     *         return this.age;
     *     }
     *
     *     public User$zulHzyXk() {
     *     }
     * }
     * 
     */
    @Test
    public void makeSimpleDto() {
        DynamicType.Builder dynamicBuilder = new ByteBuddy()
                //指定前缀
                .with(new NamingStrategy.SuffixingRandom("com.valsong.dynamicdto.User"))
                .subclass(Object.class)
                //.name("com.valsong.dynamicdto.User")
                .defineProperty("name", String.class)
                .defineProperty("age", Integer.class);


        DynamicType.Unloaded<?> dynamicType = dynamicBuilder.make();

        saveAndLoad(dynamicType);

    }

    /**
     * 构建
     * <p>
     * package com.valsong.dynamicdto;
     * <p>
     * import com.valsong.bytebuddy.MyAnnotation;
     *
     * @MyAnnotation( name = "House",
     * value = "House",
     * required = false
     * )
     * public class House {
     * @MyAnnotation( name = "address",
     * value = "china shanghai",
     * required = true
     * )
     * private String address;
     * @MyAnnotation( name = "area",
     * value = "100",
     * required = true
     * )
     * private Double area;
     * <p>
     * public void setAddress(String var1) {
     * this.address = var1;
     * }
     * <p>
     * public String getAddress() {
     * return this.address;
     * }
     * <p>
     * public void setArea(Double var1) {
     * this.area = var1;
     * }
     * <p>
     * public Double getArea() {
     * return this.area;
     * }
     * <p>
     * public House() {
     * }
     * }
     */
    @Test
    public void makeSimpleDtoWithAnnotation() {

        MyAnnotation myAnnotation = new MyAnnotation() {

            @Override
            public Class<? extends Annotation> annotationType() {
                return MyAnnotation.class;
            }

            @Override
            public String value() {
                return "House";
            }

            @Override
            public String name() {
                return "House";
            }

            @Override
            public boolean required() {
                return false;
            }
        };


        AnnotationDescription apiModelPropertyDescription1 =
                AnnotationDescription.Builder.ofType(MyAnnotation.class)
                        .define("value", "china shanghai")
                        .define("name", "address")
                        .define("required", true)
                        .build();


        //将MyAnnotation注解转化成apiModelPropertyDescription
        AnnotationDescription apiModelPropertyDescription2 =
                AnnotationDescription.Builder.ofType(MyAnnotation.class)
                        .define("value", "100")
                        .define("name", "area")
                        .define("required", true)
                        .build();


        DynamicType.Builder dynamicBuilder = new ByteBuddy()
                .subclass(Object.class)
                .name("com.valsong.dynamicdto.House")
                .annotateType(myAnnotation)
                .defineProperty("address", String.class)
                .annotateField(apiModelPropertyDescription1)
                .defineProperty("area", Double.class)
                .annotateField(apiModelPropertyDescription2);


        DynamicType.Unloaded<?> dynamicType = dynamicBuilder.make();

        saveAndLoad(dynamicType);

    }

    /**
     * 构建
     * <p>
     * package com.valsong.dynamicdto;
     * <p>
     * import java.util.Map;
     * <p>
     * public class MapContainer {
     * private Map<String, Object> map;
     * <p>
     * public void setMap(Map<String, Object> var1) {
     * this.map = var1;
     * }
     * <p>
     * public Map<String, Object> getMap() {
     * return this.map;
     * }
     * <p>
     * public MapContainer() {
     * }
     * }
     *
     * @throws ClassNotFoundException
     */
    @Test
    public void makeGenericMapDto() throws ClassNotFoundException {


        Class<?> rawType = Map.class;


        List<TypeDefinition> typeArgumentsDefinitions = new ArrayList<>(2);

        typeArgumentsDefinitions.add(new TypeDescription.ForLoadedType(String.class));
        typeArgumentsDefinitions.add(new TypeDescription.ForLoadedType(Object.class));

        //ownerType泛型
        TypeDescription.Generic ownerTypeGeneric = Optional.ofNullable(rawType.getDeclaringClass())
                .map(TypeDefinition.Sort::describe)
                .orElse(null);

        //Map<String,Object>
        TypeDefinition mapField = TypeDescription.Generic.Builder.parameterizedType(
                //TypeDescription.ForLoadedType.of(rawType)
                //为了解决版本冲突将bytebuddy版本改成了1.7.11
                new TypeDescription.ForLoadedType(rawType)
                , ownerTypeGeneric, typeArgumentsDefinitions).build();


        DynamicType.Builder dynamicBuilder = new ByteBuddy()
                .subclass(Object.class)
                .name("com.valsong.dynamicdto.MapContainer")
                .defineProperty("map", mapField);


        DynamicType.Unloaded<?> dynamicType = dynamicBuilder.make();

        saveAndLoad(dynamicType);


    }


    /**
     * 构建
     * <p>
     * package com.valsong.dynamicdto;
     * <p>
     * import java.util.List;
     * <p>
     * public class ListArrayContainer {
     * private List<String>[] listArray;
     * <p>
     * public void setListArray(List<String>[] var1) {
     * this.listArray = var1;
     * }
     * <p>
     * public List<String>[] getListArray() {
     * return this.listArray;
     * }
     * <p>
     * public ListArrayContainer() {
     * }
     * }
     *
     * @throws ClassNotFoundException
     */
    @Test
    public void makeGenericArrayType() throws ClassNotFoundException {


        Class<?> rawType = List.class;


        List<TypeDefinition> typeArgumentsDefinitions = new ArrayList<>(2);

        typeArgumentsDefinitions.add(new TypeDescription.ForLoadedType(String.class));

        //ownerType泛型
        TypeDescription.Generic ownerTypeGeneric = Optional.ofNullable(rawType.getDeclaringClass())
                .map(TypeDefinition.Sort::describe)
                .orElse(null);

        //List<String> []
        TypeDefinition listArrayField = TypeDescription.Generic.Builder.parameterizedType(
                //TypeDescription.ForLoadedType.of(rawType)
                //为了解决版本冲突将bytebuddy版本改成了1.7.11
                new TypeDescription.ForLoadedType(rawType)
                , ownerTypeGeneric, typeArgumentsDefinitions)
                .asArray()
                .build();


        DynamicType.Builder dynamicBuilder = new ByteBuddy()
                .subclass(Object.class)
                .name("com.valsong.dynamicdto.ListArrayContainer")
                .defineProperty("listArray", listArrayField);


        DynamicType.Unloaded<?> dynamicType = dynamicBuilder.make();

        saveAndLoad(dynamicType);


    }


    /**
     * 构建
     * <p>
     * package com.valsong.dynamicdto;
     * <p>
     * import java.util.LinkedHashMap;
     * import java.util.Map;
     * <p>
     * public class WildcardTypeContainer {
     * private Map<? extends Number, ? super LinkedHashMap>[] map;
     * <p>
     * public void setMap(Map<? extends Number, ? super LinkedHashMap>[] var1) {
     * this.map = var1;
     * }
     * <p>
     * public Map<? extends Number, ? super LinkedHashMap>[] getMap() {
     * return this.map;
     * }
     * <p>
     * public WildContainer() {
     * }
     * }
     *
     * @throws ClassNotFoundException
     */
    @Test
    public void makeWildcardType() throws ClassNotFoundException {


        Class<?> rawType = Map.class;


        List<TypeDefinition> type1ArgumentsDefinitions = new ArrayList<>(2);


        // ? extends Number
        type1ArgumentsDefinitions.add(TypeDescription.Generic.OfWildcardType.Latent
                .boundedAbove(new TypeDescription.ForLoadedType(Number.class).asGenericType(),
                        AnnotationSource.Empty.INSTANCE));


        // ? super LinkedHashMap
        type1ArgumentsDefinitions.add(TypeDescription.Generic.OfWildcardType.Latent
                .boundedBelow(new TypeDescription.ForLoadedType(LinkedHashMap.class).asGenericType(),
                        AnnotationSource.Empty.INSTANCE));

        //ownerType泛型
        TypeDescription.Generic ownerTypeGeneric = Optional.ofNullable(rawType.getDeclaringClass())
                .map(TypeDefinition.Sort::describe)
                .orElse(null);

        //Map<? extends Number, ? super LinkedHashMap>
        TypeDefinition mapField = TypeDescription.Generic.Builder.parameterizedType(
                //TypeDescription.ForLoadedType.of(rawType)
                //为了解决版本冲突将bytebuddy版本改成了1.7.11
                new TypeDescription.ForLoadedType(rawType)
                , ownerTypeGeneric, type1ArgumentsDefinitions)
                .asArray()
                .build();


        DynamicType.Builder dynamicBuilder = new ByteBuddy()
                .subclass(Object.class)
                .name("com.valsong.dynamicdto.WildcardTypeContainer")
                .defineProperty("map", mapField);


        DynamicType.Unloaded<?> dynamicType = dynamicBuilder.make();

        saveAndLoad(dynamicType);


    }

    /**
     * 构建
     * <p>
     * import java.util.List;
     * <p>
     * public class Container<T, K extends Number> {
     * private List<T>[] ts;
     * private K k;
     * <p>
     * public void setTs(List<T>[] var1) {
     * this.ts = var1;
     * }
     * <p>
     * public List<T>[] getTs() {
     * return this.ts;
     * }
     * <p>
     * public void setK(K var1) {
     * this.k = var1;
     * }
     * <p>
     * public K getK() {
     * return this.k;
     * }
     * <p>
     * public Container() {
     * }
     * }
     */
    @Test
    public void makeTypeVariable() {

        Class<?> rawType = List.class;

        //T
        TypeDefinition tGeneric = TypeDescription.Generic.Builder.typeVariable("T").build();


        List<TypeDefinition> typeArgumentsDefinitions = new ArrayList<>(2);

        typeArgumentsDefinitions.add(tGeneric);

        //ownerType泛型
        TypeDescription.Generic ownerTypeGeneric = Optional.ofNullable(rawType.getDeclaringClass())
                .map(TypeDefinition.Sort::describe)
                .orElse(null);

        //List<T>
        TypeDefinition tsField = TypeDescription.Generic.Builder.parameterizedType(
                //TypeDescription.ForLoadedType.of(rawType)
                //为了解决版本冲突将bytebuddy版本改成了1.7.11
                new TypeDescription.ForLoadedType(rawType)
                , ownerTypeGeneric, typeArgumentsDefinitions)
                .asArray()
                .build();

        //K
        TypeDefinition kGeneric = TypeDescription.Generic.Builder.typeVariable("K").build();

        DynamicType.Builder dynamicBuilder = new ByteBuddy()
                .subclass(Object.class)
                .name("com.valsong.dynamicdto.Container")

                .typeVariable("T")
                .typeVariable("K", Number.class)

                .defineProperty("ts", tsField)
                .defineProperty("k", kGeneric);


        DynamicType.Unloaded<?> dynamicType = dynamicBuilder.make();

        saveAndLoad(dynamicType);

    }


    /**
     * 构建
     * <p>
     * package com.valsong.dynamicdto;
     * <p>
     * import java.util.LinkedHashMap;
     * import java.util.List;
     * import java.util.Map;
     * <p>
     * public class GenericCollectionContainer {
     * private List<? extends Map<String, Object>> list;
     * private Map<String, ? super LinkedHashMap<String, Integer>>[] mapArray;
     * <p>
     * public void setList(List<? extends Map<String, Object>> var1) {
     * this.list = var1;
     * }
     * <p>
     * public List<? extends Map<String, Object>> getList() {
     * return this.list;
     * }
     * <p>
     * public void setMapArray(Map<String, ? super LinkedHashMap<String, Integer>>[] var1) {
     * this.mapArray = var1;
     * }
     * <p>
     * public Map<String, ? super LinkedHashMap<String, Integer>>[] getMapArray() {
     * return this.mapArray;
     * }
     * <p>
     * public GenericCollectionContainer() {
     * }
     * }
     *
     * @throws ClassNotFoundException
     */
    @Test
    public void makeGenericDtoUseType() throws ClassNotFoundException {


        Type listType = new TypeReference<List<? extends Map<String, Object>>>() {
        }.getType();

        Type mapArrayType = new TypeReference<Map<String, ? super LinkedHashMap<String, Integer>>[]>() {
        }.getType();


        DynamicType.Builder dynamicBuilder = new ByteBuddy()
                .subclass(Object.class)
                .name("com.valsong.dynamicdto.GenericCollectionContainer")
                .defineProperty("list", listType)
                .defineProperty("mapArray", mapArrayType);


        DynamicType.Unloaded<?> dynamicType = dynamicBuilder.make();

        saveAndLoad(dynamicType);


    }

    /**
     * package com.valsong.dynamicdto;
     * <p>
     * import com.valsong.bytebuddy.ByteBuddyTest.DataConverterExecutor;
     * import java.lang.reflect.Type;
     * <p>
     * public class DataConverter {
     * public static String toJson(Object obj) {
     * return DataConverterExecutor.toJson(var0);
     * }
     * <p>
     * public static Object fromJson(String json, Type type) {
     * return DataConverterExecutor.fromJson(var0, var1);
     * }
     * <p>
     * public DataConverter() {
     * }
     * }
     */
    @Test
    public void makeMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        DynamicType.Builder dynamicBuilder = new ByteBuddy()
                .subclass(Object.class)
                .name("com.valsong.dynamicdto.DataConverter");

        dynamicBuilder = dynamicBuilder.defineMethod("toJson", String.class, Modifier.PUBLIC + Modifier.STATIC)
                .withParameter(Object.class, "obj")
                .intercept(MethodDelegation.to(DataConverterExecutor.class))

                .defineMethod("fromJson", Object.class, Modifier.PUBLIC + Modifier.STATIC)
                .withParameter(String.class, "json")
                .withParameter(Type.class, "type")
                .intercept(MethodDelegation.to(DataConverterExecutor.class));

        DynamicType.Unloaded<?> dynamicType = dynamicBuilder.make();

        Class<?> clazz = saveAndLoad(dynamicType);
        User user = User.newBuilder()
                .name("val")
                .age(27)
                .height(172.0)
                .email("valsong@163.com")
                .build();

        Method toJsonMethod = clazz.getMethod("toJson", Object.class);
        String json = (String) toJsonMethod.invoke(null, user);
        System.out.println(json);

        Assertions.assertNotNull(json);

        Method fromJsonMethod = clazz.getMethod("fromJson", String.class, Type.class);
        User user2 = (User) fromJsonMethod.invoke(null, json, User.class);
        Assertions.assertEquals(user, user2);

    }


    /**
     * 保存到本地目录,并且加载到类加载器中
     *
     * @param dynamicType
     */
    private Class<?> saveAndLoad(DynamicType.Unloaded<?> dynamicType) {
        //写入到本地目录
        try {
            dynamicType.saveIn(new File("target/classes"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        return dynamicType.load(Thread.currentThread().getContextClassLoader(),
                ClassLoadingStrategy.Default.INJECTION).getLoaded();
    }


    public static class DataConverterExecutor {


        public static String toJson(Object obj) {
            return JSON.toJSONString(obj);
        }


        public static <T> T fromJson(@Argument(0) String json, @Argument(1) Type type) {
            return JSON.parseObject(json, type);
        }

    }

}

上述测试用例用到的DTO和自定义注解

package com.valsong.dto;

import java.util.Objects;

public class User {

    private String name;

    private Integer age;

    private Double height;

    private String email;

    public User() {
    }

    private User(Builder builder) {
        setName(builder.name);
        setAge(builder.age);
        setHeight(builder.height);
        setEmail(builder.email);
    }

    public static Builder newBuilder() {
        return new Builder();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Double getHeight() {
        return height;
    }

    public void setHeight(Double height) {
        this.height = height;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }


    public static final class Builder {
        private String name;
        private Integer age;
        private Double height;
        private String email;

        private Builder() {
        }

        public Builder name(String val) {
            name = val;
            return this;
        }

        public Builder age(Integer val) {
            age = val;
            return this;
        }

        public Builder height(Double val) {
            height = val;
            return this;
        }

        public Builder email(String val) {
            email = val;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                ", email='" + email + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(name, user.name) &&
                Objects.equals(age, user.age) &&
                Objects.equals(height, user.height) &&
                Objects.equals(email, user.email);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, height, email);
    }
}

package com.valsong.bytebuddy;


import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE,ElementType.TYPE_PARAMETER,ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {

    String value() default "";

    String name() default "";

    boolean required() default false;
}

转载于:https://my.oschina.net/valsong/blog/2988403

### 使用 ByteBuddy 创建的类或方法 为了使用 ByteBuddy 动态创建的类或方法,可以利用 `net.bytebuddy.dynamic.DynamicType.Builder` 接口及其子接口所提供的功能。这些接口允许定义新的类以及其成员的方法签名,包括参数化类。 当构建一个新的动态类时,可以通过指定目标类的父类和实现的接口来引入信息。对于方法而言,在定义新方法的过程中声明该方法所使用的类变量即可完成的支持[^1]。 下面是一个简单的例子,展示如何使用 ByteBuddy 来创建一个具有属性的类: ```java import net.bytebuddy.ByteBuddy; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.implementation.MethodDelegation; public class GenericClassCreator { public static void main(String[] args) throws Exception { Class<?> dynamicType = new ByteBuddy() .subclass(Object.class) // 定义T extends Number .defineGenericParameter("T", TypeDescription.Generic.Builder.parameterizedType(Number.class).build()) // 添加字段, 类为 T (即Number的一个具体子类) .defineField("value", TypeDefinition.ForLoadedType.ofNullable(Integer.class), Visibility.PRIVATE) // 实现toString() 方法并返回 value 字段的内容作为字符串形式. .method(ElementMatchers.named("toString")) .intercept(MethodDelegation.to(new ToStringInterceptor())) .make() .load(GenericClassCreator.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded(); System.out.println(dynamicType); } } ``` 在这个案例里,首先通过 `.defineGenericParameter()` 方法指定了名为"T" 的参数,并将其限定为 `Number` 及其派生类;接着在`.defineField()` 中应用了这个参数给具体的字段。最后实现了对象自身的 `toString()` 方法以便于测试输出效果。 关于创建的方法,则可以在定义此类的新方法时直接设置相应的类形参列表: ```java // 继续上述代码... .defineMethod("getValueAsDouble", double.class, Modifier.PUBLIC) .withParameters(TypeVariableToken.forName("T")) // 这里的 "T" 是之前定义好的名称 .intercept(FixedValue.value(0D)) .make(); ``` 这里展示了如何向自定义的方法添加单个参数的方式。需要注意的是实际操作中可能还需要考虑更多细节比如边界条件处理等[^2]。 #### 注意事项 - 的实际类擦除机制意味着编译后的字节码并不保留所有的信息。因此,在某些场景下,如果需要获取完整的描述符,应该保存额外的信息或者依赖其他方式间接推断。 - 当涉及到复杂的多层嵌套结构时,建议仔细设计以确保最终生成的目标类能够满足预期的功能需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值