java

javassist动态增加类属性

2020-09-28  本文已影响0人  修行者12138

引入pom

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.27.0-GA</version>
</dependency>

Student.class

@Data
public class Student{
    @Id
    private Long id;

    private String studentName;

    private Integer age;
}
@Test
public void test1() throws Exception {
    Student student = new Student();
    student.setStudentName("ltm");
    student.setAge(99);

    // 需要新增的属性
    Map<String, Object> newFieldMap = new HashMap<>();
    newFieldMap.put("className", "1班");

    ClassPool pool = ClassPool.getDefault();
    // 注意: 不能用pool.get(Student.class.getName()),否则报错attempted  duplicate class definition for name,原因是同个Class不能在同个ClassLoader中加载两次
    CtClass ctClass = pool.get(student.getClass().getName());

    // 给class新增属性
    for(String fieldName: newFieldMap.keySet()) {
        // 字段类型
        String fieldType = newFieldMap.get(fieldName).getClass().getName();
        // 创建新字段
        CtField ctField = new CtField(pool.get(fieldType), fieldName, ctClass);
        ctField.setModifiers(Modifier.PRIVATE);
        ctClass.addField(ctField);
    }

    // 同个Class不能在同个ClassLoader中加载两次,所以需要使用javassist提供的ClassLoader
    Loader classLoader = new Loader(pool);
    // 生成新的字节码
    Class<?> newClass = classLoader.loadClass(ctClass.getName());
    // 用新的字节码创建新的对象
    Object newObject = newClass.newInstance();

    // 设置原有属性的值
    for (Field oldField: student.getClass().getDeclaredFields()) {
        // 跳过final属性,因为final属性无法修改
        if (java.lang.reflect.Modifier.isFinal(oldField.getModifiers())) {
            continue;
        }
        oldField.setAccessible(true);
        Field newField = newClass.getDeclaredField(oldField.getName());
        newField.setAccessible(true);
        newField.set(newObject, oldField.get(student));
    }

    // 设置新增属性的值
    for (String fieldName: newFieldMap.keySet()) {
        Field field = newClass.getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(newObject, newFieldMap.get(fieldName));
    }

    for (Field field: newClass.getDeclaredFields()) {
        field.setAccessible(true);
        System.out.println(field.getName() + ": " + field.get(newObject));
    }
}

输出结果
id: null
studentName: ltm
age: 99
className: 1班

本方案并不是在已实例化的对象上直接增加属性,而是修改字节码,给字节码增加属性,用新的字节码创建新的对象,然后给新对象的属性赋值。

上一篇 下一篇

猜你喜欢

热点阅读