Javassist

<!-- https://mvnrepository.com/artifact/javassist/javassist -->
<dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.12.1.GA</version>
</dependency>
package com.noob;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException;
import lombok.Data;

import com.alibaba.fastjson.JSON;

public class TestJavassist {

    public static void main(String[] args) {
        try {

            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            ObjectOutputStream readIn = new ObjectOutputStream(bo);
            readIn.writeObject(modifyClass());
            ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
            ObjectInputStream writeOut = new ObjectInputStream(bi);
            A a = A.class.cast(writeOut.readObject()); // 反序列化对象类型是序列化对象的父类或自己
            System.out.println("序列化对象:---" + JSON.toJSONString(a));
            System.out.println(a.getA_a());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Object modifyClass() throws NotFoundException, CannotCompileException, IntrospectionException,
            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
      /*  System.out.println("-----------修改前的A.class信息--------------------");
        for (Method method : A.class.getDeclaredMethods()) {
            System.out.println("method:----" + method.getName());
        }
        for (Field field : A.class.getDeclaredFields()) {
            System.out.println("field:----" + field.getName());
        }*/

        ClassPool pool = ClassPool.getDefault();
        // 获取一个A类的CtClass对象
        CtClass ctClass = pool.get("com.noob.A");
        // 为ctClass设置一个父类
        ctClass.setSuperclass(pool.get("com.noob.B"));
        // 为cTclass对象添加一个属性name
        ctClass.addField(CtField.make("private String addField;", ctClass));
        ctClass.addMethod(CtMethod.make("public void setAddField(String addField){this.addField = addField;}", ctClass));
        ctClass.addMethod(CtMethod.make("public String getAddField(){return this.addField;}", ctClass));

        ctClass.removeField(ctClass.getDeclaredField("A_a"));
        ctClass.removeMethod(ctClass.getDeclaredMethod("getA_a"));
        ctClass.removeMethod(ctClass.getDeclaredMethod("setA_a")); // 把 A.class 中的A_a 相关信息删掉.

        // 获取ctClass对象对应的Class对象cls
        Class cls = ctClass.toClass();
        // 对cls类进行内省,获得对象BeanInfo
        BeanInfo beanInfo = Introspector.getBeanInfo(cls, Object.class);
        System.out.println("-----------Javassist 修改操做对象信息--------------------");
        MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors(); // 获取beanInfo的方法描述对象
        for (int i = 0; i < methodDescriptors.length; i++) {
            System.out.println("method:----" + methodDescriptors[i].getName());
        }

        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();// 获取beanInfo的属性描述对象
        for (int i = 0; i < propertyDescriptors.length; i++) {
            System.out.println("field:----" + propertyDescriptors[i].getDisplayName());
        }

        System.out.println("-----------修改后直接经过A.class获取信息--------------------");
        for (Method method : A.class.getDeclaredMethods()) {
            System.out.println("method:----" + method.getName());
        }
        for (Field field : A.class.getDeclaredFields()) {
            System.out.println("field:----" + field.getName());
        }

        Object obj = cls.newInstance();
        for (Method method : cls.getDeclaredMethods()) {
            if (method.getName().startsWith("set")) {
                method.invoke(obj, "test" + method.getName());
            }
        }
        return obj;
    }
}

@Data
class A implements Serializable {

    private static final long serialVersionUID = 1L;
    private String            A_a, A_b, A_c;

}

@Data
class B implements Serializable {

    private static final long serialVersionUID = 1L;
    private String            B_d, B_e, B_f;

}
-----------Javassist 修改操做对象信息--------------------
method:----setB_e
method:----getAddField
method:----setB_f
method:----setB_d
method:----getB_d
method:----getB_e
method:----getB_f
method:----setA_b
method:----setA_c
method:----canEqual
method:----getA_b
method:----getA_c
method:----setAddField
field:----a_b
field:----a_c
field:----addField
field:----b_d
field:----b_e
field:----b_f
-----------修改后直接经过A.class获取信息--------------------
method:----equals
method:----toString
method:----hashCode
method:----getA_b
method:----getA_c
method:----setA_b
method:----setA_c
method:----canEqual
method:----setAddField
method:----getAddField
field:----serialVersionUID
field:----A_b
field:----A_c
field:----addField
序列化对象:---{"a_b":"testsetA_b","a_c":"testsetA_c","addField":"testsetAddField"}
Exception in thread "main" java.lang.NoSuchMethodError: com.noob.A.getA_a()Ljava/lang/String;
	at com.noob.TestJavassist.main(TestJavassist.java:39)

【新增 字段、方法】java

【删除 字段、方法】json

 

测试结论:
经过javassist修改A.class以后:测试

  1. 删除了关于“A_a”的属性和方法。
  2. 增长了关于“addField”的属性和方法。
  3. 经过A.class 直接获取类信息仍旧是被修改以后的信息。
  4. 若是在修改以前经过反射获取A.class的内容,修改以后再获取内容将报错(可见在JVM运行中com.noob.A.class的类文件确实只能有一次加载

-----------修改前的A.class信息--------------------
method:----equals
method:----toString
method:----hashCode
method:----getA_a
method:----setA_a
method:----getA_b
method:----getA_c
method:----setA_b
method:----setA_c
method:----canEqual
field:----serialVersionUID
field:----A_a
field:----A_b
field:----A_c
javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "com/noob/A"
	at javassist.ClassPool.toClass(ClassPool.java:1085)
	at javassist.ClassPool.toClass(ClassPool.java:1028)
	at javassist.ClassPool.toClass(ClassPool.java:986)
	at javassist.CtClass.toClass(CtClass.java:1079)
	at com.noob.TestJavassist.modifyClass(TestJavassist.java:71)
	at com.noob.TestJavassist.main(TestJavassist.java:34)
Caused by: java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "com/noob/A"
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at javassist.ClassPool.toClass2(ClassPool.java:1098)
	at javassist.ClassPool.toClass(ClassPool.java:1079)
	... 5 more
javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "com/noob/A"
	at javassist.ClassPool.toClass(ClassPool.java:1085)
	at javassist.ClassPool.toClass(ClassPool.java:1028)
	at javassist.ClassPool.toClass(ClassPool.java:986)
	at javassist.CtClass.toClass(CtClass.java:1079)
	at com.noob.TestJavassist.modifyClass(TestJavassist.java:71)
	at com.noob.TestJavassist.main(TestJavassist.java:34)
Caused by: java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "com/noob/A"
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at javassist.ClassPool.toClass2(ClassPool.java:1098)
	at javassist.ClassPool.toClass(ClassPool.java:1079)
	... 5 more
javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "com/noob/A"
	at javassist.ClassPool.toClass(ClassPool.java:1085)
	at javassist.ClassPool.toClass(ClassPool.java:1028)
	at javassist.ClassPool.toClass(ClassPool.java:986)
	at javassist.CtClass.toClass(CtClass.java:1079)
	at com.noob.TestJavassist.modifyClass(TestJavassist.java:71)
	at com.noob.TestJavassist.main(TestJavassist.java:34)
Caused by: java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "com/noob/A"
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at javassist.ClassPool.toClass2(ClassPool.java:1098)
	at javassist.ClassPool.toClass(ClassPool.java:1079)
	... 5 more
相关文章
相关标签/搜索