【问题标题】:Add Lombok annotations dynamically using Javassist使用 Javassist 动态添加 Lombok 注释
【发布时间】:2021-10-19 06:42:23
【问题描述】:

对于一个项目,我正在试验 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;
        }
    }
}

我可以使用 createClass 方法创建类。在使用反射 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);
}

输出

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="")

从输出中可以看出,spring.Componentjavalang.Deprecated 已添加,但 lombok.Data 未添加。 p>

pom.xml

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

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

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

我哪里做错了?

谢谢。

【问题讨论】:

    标签: java spring reflection lombok javassist


    【解决方案1】:

    TL;DR

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

    为什么?

    这不起作用,因为 lombok 使用注释处理器(请参阅 thisthat)。

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

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

    如果你查看像@Data这样的lombok注解的javadoc,你可以看到以下内容:

    @Retention(源)

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

    【讨论】:

    • 感谢您的意见。有什么办法可以修改注释保留?或者我们可以调用任何方法来检查此注释并添加 Lombok 的 getter/setter 方法。我实际上尝试检查 Lombok 源代码库,但无法弄清楚。
    • 不,你不能。此外,如果保留为 RUNTIME,lombok 甚至不会在运行时关心它
    • @JayachandraCh 这看起来很像xy problem。你想达到什么目标?生成hashCodeequalstoString 之类的方法?好吧,您已经在使用代码操作库了……但是您应该知道,通常不能在运行时将成员添加到已加载的类中。除此之外,当没有代码实际使用它们时,在运行时生成 getter、setter 和构造函数没有任何意义,因为它们在编译时不存在。此外,依赖 Lombok 的不完整类无论如何都不会被编译。
    猜你喜欢
    • 2012-07-31
    • 1970-01-01
    • 1970-01-01
    • 2014-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-02
    • 1970-01-01
    相关资源
    最近更新 更多