我这人文笔很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); } /** * 构建 ** package com.valsong.dynamicdto; *
* 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; *
* public void setAddress(String var1) { * this.address = var1; * } *
* public String getAddress() { * return this.address; * } *
* public void setArea(Double var1) { * this.area = var1; * } *
* public Double getArea() { * return this.area; * } *
* public House() { * } * } */ @Test public void makeSimpleDtoWithAnnotation() { MyAnnotation myAnnotation = new MyAnnotation() { @Override public Class
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); } /** * 构建 ** package com.valsong.dynamicdto; *
* import java.util.Map; *
* public class MapContainer { * private Map
map; * * public void setMap(Map
var1) { * this.map = var1; * } * * public Map
getMap() { * return this.map; * } * * public MapContainer() { * } * } * * @throws ClassNotFoundException */ @Test public void makeGenericMapDto() throws ClassNotFoundException { Class
rawType = Map.class; ListtypeArgumentsDefinitions = 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 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); } /** * 构建 * * package com.valsong.dynamicdto; *
* import java.util.List; *
* public class ListArrayContainer { * private List
[] listArray; * * public void setListArray(List
[] var1) { * this.listArray = var1; * } * * public List
[] getListArray() { * return this.listArray; * } * * public ListArrayContainer() { * } * } * * @throws ClassNotFoundException */ @Test public void makeGenericArrayType() throws ClassNotFoundException { Class
rawType = List.class; ListtypeArgumentsDefinitions = 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 [] 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); } /** * 构建 * * package com.valsong.dynamicdto; *
* import java.util.LinkedHashMap; * import java.util.Map; *
* public class WildcardTypeContainer { * private Map
[] map; ** public void setMap(Map
[] var1) { * this.map = var1; * } ** public Map
[] getMap() { * return this.map; * } ** public WildContainer() { * } * } * * @throws ClassNotFoundException */ @Test public void makeWildcardType() throws ClassNotFoundException { Class
rawType = Map.class; Listtype1ArgumentsDefinitions = 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 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); } /** * 构建 * * import java.util.List; *
* public class Container
{ * private List [] ts; * private K k; * * public void setTs(List
[] var1) { * this.ts = var1; * } * * public List
[] getTs() { * return this.ts; * } * * public void setK(K var1) { * this.k = var1; * } *
* public K getK() { * return this.k; * } *
* public Container() { * } * } */ @Test public void makeTypeVariable() { Class
rawType = List.class; //T TypeDefinition tGeneric = TypeDescription.Generic.Builder.typeVariable("T").build(); ListtypeArgumentsDefinitions = new ArrayList<>(2); typeArgumentsDefinitions.add(tGeneric); //ownerType泛型 TypeDescription.Generic ownerTypeGeneric = Optional.ofNullable(rawType.getDeclaringClass()) .map(TypeDefinition.Sort::describe) .orElse(null); //List 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); } /** * 构建 * * package com.valsong.dynamicdto; *
* import java.util.LinkedHashMap; * import java.util.List; * import java.util.Map; *
* public class GenericCollectionContainer { * private List
> list; * private Map>[] mapArray; * * public void setList(List
> var1) { * this.list = var1; * } ** public List
> getList() { * return this.list; * } ** public void setMapArray(Map
>[] var1) { * this.mapArray = var1; * } * * public Map
>[] getMapArray() { * return this.mapArray; * } * * public GenericCollectionContainer() { * } * } * * @throws ClassNotFoundException */ @Test public void makeGenericDtoUseType() throws ClassNotFoundException { Type listType = new TypeReference
>>() { }.getType(); Type mapArrayType = new TypeReference
* import com.valsong.bytebuddy.ByteBuddyTest.DataConverterExecutor; * import java.lang.reflect.Type; *
* public class DataConverter { * public static String toJson(Object obj) { * return DataConverterExecutor.toJson(var0); * } *
* public static Object fromJson(String json, Type type) { * return DataConverterExecutor.fromJson(var0, var1); * } *
* 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 staticT 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)@Documentedpublic @interface MyAnnotation { String value() default ""; String name() default ""; boolean required() default false;}