我這人文筆很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;
}
複製程式碼