使用 Javassist 动态添加 Lombok 注释

Jay*_* Ch 2 java reflection spring javassist lombok

在一个项目中,我正在尝试javassist(java 二进制代码操作库)。

我能够使用下面的代码创建一个简单的 POJO 类

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我能够使用createClass方法创建类。在使用 Reflection API 检查类时,我注意到lombok.Data注释没有添加到类中。

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);
}
Run Code Online (Sandbox Code Playgroud)

输出

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="")
Run Code Online (Sandbox Code Playgroud)

从输出中可以看到spring.Componentjavalang.Deprecated已添加,但lombok.Data没有添加。

pom.xml

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <!-- <scope>annotationProcessor</scope> -->
</dependency>
Run Code Online (Sandbox Code Playgroud)

尝试将范围设置为compileruntimeprovided ,但似乎都不起作用。

使用mvn spring-boot:run在命令行中执行

我哪里做错了?

谢谢。

dan*_*1st 5

长话短说

您不能在运行时使用lombok,您需要自己生成样板代码。

为什么?

这是行不通的,因为 lombok 使用注释处理器(参见这个那个)。

这意味着lombok程序运行时。已编译。

当您尝试在运行时插入注释时,lombok 注释将不会被处理,也不会生成任何内容。

如果您查看 lombok 注释的 javadoc,例如@Data,您可以看到以下内容:

@Retention(来源)

这意味着注释在编译时可见,但它并不存在于字节码中,并且您也无法在运行时访问它。

  • @JayachandraCh 这看起来很像一个 [xy 问题](https://meta.stackexchange.com/a/66378/242352)。你想达到什么目的?生成“hashCode”、“equals”、“toString”等方法?好吧,您已经在使用代码操作库......但是您应该知道,通常不能在运行时将成员添加到已加载的类中。除此之外,当没有代码实际使用它们时,在运行时生成 getter、setter 和构造函数没有任何意义,因为它们在编译时不存在。此外,依赖 Lombok 的不完整类无论如何也不会被编译。 (3认同)