mardi 19 octobre 2021

Add Lombok annotations dynamically using Javassist

For a project I was experimenting javassist (java binary code manipulation library).

I was able to create a simple POJO class using below code

public static Class<?> createClass(ModelClass clazz) throws NotFoundException, CannotCompileException {
    ModelProperty[] properties = clazz.getProperties();

    ClassPool classPool = ClassPool.getDefault();
    CtClass ctClass = classPool.makeClass(clazz.getName());

    // add annotation
    ClassFile classFile = ctClass.getClassFile();
    ConstPool constpool = classFile.getConstPool();
    AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
    System.out.println(Data.class.getName());
    Annotation annotation = new Annotation(lombok.Data.class.getName(), constpool);
    Annotation annotation1 = new Annotation(Component.class.getName(), constpool);
    Annotation annotation2 = new Annotation(Deprecated.class.getName(), constpool);
    annotationsAttribute.setAnnotations(new Annotation[] {annotation, annotation1, annotation2});
    ctClass.getClassFile().addAttribute(annotationsAttribute);

    for (ModelProperty property : properties) {
        String camelCaseField = property.getName().substring(0, 1).toUpperCase() + property.getName().substring(1);
        CtField ctField = new CtField(resolveCtClass(property.getType()), property.getName(), ctClass);
        ctClass.addField(ctField);

        // add getter
        // CtMethod fieldGetter = CtNewMethod.getter("get" + camelCaseField, ctField);
        // ctClass.addMethod(fieldGetter);

        // add setter
        // CtMethod fieldSetter = CtNewMethod.setter("set" + camelCaseField, ctField);
        // ctClass.addMethod(fieldSetter);
    }

    return ctClass.toClass();
}

// ModelClass
@Data
public class ModelClass {

    private String name;

    private ModelProperty[] properties;

    public void setName(String name) {
        this.name = name;
    }

    public void setModelProperty(ModelProperty[] properties) {
        this.properties = properties;
    }

}

// ModelProperty
@Data
public class ModelProperty {

    private String name;

    private Class<?> type;

    public void setType(String type) {
        switch (type.toLowerCase()) {
            case "int":
            case "integer":
                this.type = Integer.class;
                break;
            case "float":
                this.type = Float.class;
                break;
            case "str":
            case "string":
                this.type = String.class;
                break;
            case "bool":
            case "boolean":
                this.type = Boolean.class;
                break;
            default:
                this.type = String.class;
                break;
        }
    }
}

I was able to create class by using createClass method. While inspecting class with Reflection API, I've noticed that lombok.Data annotation is not adding to class.

Class<?> clazz = PojoCreator.createClass(modelClassPerson);

for (final Method method : clazz.getMethods()) {
    System.out.println(method);
}
for (final Annotation annotation : clazz.getAnnotations()) {
    System.out.println(annotation);
}
for (final Method method : clazz.getDeclaredMethods()) {
    System.out.println(method);
}
for (final Annotation annotation : clazz.getDeclaredAnnotations()) {
    System.out.println(annotation);
}

Output

public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
@org.springframework.stereotype.Component(value="")
@java.lang.Deprecated(forRemoval=false, since="")
@org.springframework.stereotype.Component(value="")
@java.lang.Deprecated(forRemoval=false, since="")

As you can see from the output spring.Component and javalang.Deprecated is added but lombok.Data didn't.

pom.xml

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <!-- <scope>annotationProcessor</scope> -->
</dependency>

Tried setting scope as compile, runtime, provided but none seems to work.

Using mvn spring-boot:run to execute in command line

Where did I go wrong?

Thanks.





Aucun commentaire:

Enregistrer un commentaire