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
相關文章
相關標籤/搜索