Wednesday, 29 April 2020

Javassist: Modify type of instance field

It is a two-step process.
a.   Remove the field
b.   Create a filed with same name with different type

Follow below step-by-step procedure to modify type of a field.

Step 1: Get ClassPool instance.
ClassPool classPool = ClassPool.getDefault();

Step 2: Get CtClass instance.
CtClass pointClass = classPool.get("com.sample.app.model.Point");

Step 3: Remove the field from CtClass instance.
CtField toBeDeleted = pointClass.getField("x");
pointClass.removeField(toBeDeleted);

Step 4: Create field with same with different data type.
CtField xField = new CtField(CtClass.doubleType, "x", pointClass);
xField.setModifiers(Modifier.PUBLIC);

Step 5: Add new field to the class.
pointClass.addField(xField);

Step 6: Publish modified byte code
pointClass.toClass();

Find the below working application.

Point.java
package com.sample.app.model;

public class Point {
    public int x;
    public int y;

}

App.java
package com.sample.app;

import java.lang.reflect.Field;

import com.sample.app.model.Point;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;

public class App {

    public static boolean set(Object object, String fieldName, Object fieldValue) {
        Class<?> clazz = object.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(object, fieldValue);
                return true;
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    public static <V> V get(Object object, String fieldName) {
        Class<?> clazz = object.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return (V) field.get(object);
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
        }
        return null;
    }

    public static void main(String args[]) throws Exception {
        ClassPool classPool = ClassPool.getDefault();

        CtClass pointClass = classPool.get("com.sample.app.model.Point");

        CtField toBeDeleted = pointClass.getField("x");
        pointClass.removeField(toBeDeleted);

        CtField xField = new CtField(CtClass.doubleType, "x", pointClass);
        xField.setModifiers(Modifier.PUBLIC);
        pointClass.addField(xField);

        // Publish modified byte code
        pointClass.toClass();

        pointClass.writeFile("/Users/Shared/javassist");
        Point point = new Point();

        set(point, "x", 1.2345);
        Object value = get(point, "x");

        System.out.println("Value of x is " + value);
    }

}

Run App.java, you will see below message in console.
Value of x is 1.2345

You can observe that Point.class file is created at /Users/Shared/javassist.

$tree /Users/Shared/javassist
/Users/Shared/javassist
└── com
    └── sample
        └── app
            └── model
                └── Point.class

4 directories, 1 file

Open Point.class in any java decompiler to see the source code.



Previous                                                    Next                                                    Home

No comments:

Post a Comment