使用Javassist动态添加Lombok @Data注解失败问题求助
Let's break down exactly what's happening here, and how to fix it:
1. The Retention Policy Problem
First, let's look at the root reason you can't see the @Data annotation via reflection: Lombok's @Data uses @Retention(RetentionPolicy.SOURCE).
Annotations with SOURCE retention exist only in Java source code—they’re completely discarded during compilation and never make it into the final bytecode. When you use Javassist to add this annotation to a runtime-generated class file, the JVM ignores it entirely because the retention policy doesn’t allow it to be preserved in bytecode.
Compare this to @Component and @Deprecated, which use @Retention(RetentionPolicy.RUNTIME)—these annotations are kept in the bytecode, so reflection can pick them up without issue.
2. Lombok's Compile-Time Only Limitation
Even if @Data had runtime retention, it still wouldn’t generate the getter/setter, toString(), equals(), or hashCode() methods you expect. Here’s why:
Lombok is a compile-time annotation processor. It hooks into the Java compiler’s pipeline, scans your .java source files for annotations, and modifies the abstract syntax tree (AST) to generate corresponding code before bytecode is created.
When you generate a class dynamically at runtime with Javassist, the compilation phase is long over. Lombok’s processor never gets a chance to scan or modify this dynamic class—so no methods are generated, even if you could attach the annotation.
Solutions to Get "Lombok-Like" Behavior for Dynamic POJOs
Option 1: Manually Generate Methods with Javassist
Since you can’t rely on Lombok, use Javassist’s built-in tools to create the methods @Data would normally generate.
First, uncomment your getter/setter generation code:
// 添加getter CtMethod fieldGetter = CtNewMethod.getter("get" + camelCaseField, ctField); ctClass.addMethod(fieldGetter); // 添加setter CtMethod fieldSetter = CtNewMethod.setter("set" + camelCaseField, ctField); ctClass.addMethod(fieldSetter);
Then, add methods like toString(), equals(), and hashCode() manually. Here’s a simple example for toString():
// 生成toString方法 StringBuilder toStringBody = new StringBuilder("public String toString() { "); toStringBody.append("StringBuilder sb = new StringBuilder(\"").append(clazz.getName()).append("{\"); "); for (ModelProperty prop : properties) { toStringBody.append("sb.append(\"").append(prop.getName()).append("=\"); "); toStringBody.append("sb.append(this.").append(prop.getName()).append("); "); toStringBody.append("sb.append(\", \"); "); } toStringBody.append("sb.setLength(sb.length() - 2); "); // 移除最后的逗号空格 toStringBody.append("sb.append(\"}\"); "); toStringBody.append("return sb.toString(); }"); CtMethod toStringMethod = CtNewMethod.make(toStringBody.toString(), ctClass); ctClass.addMethod(toStringMethod);
You can extend this pattern to implement equals() and hashCode() by checking each field’s value explicitly.
Option 2: Use a Runtime Bytecode Library Like Byte Buddy
If writing all those methods manually feels tedious, libraries like Byte Buddy are designed for runtime bytecode manipulation and can generate Lombok-style methods with less code.
Here’s a quick example of generating a POJO with getters, setters, and a toString() method using Byte Buddy:
import net.bytebuddy.ByteBuddy; import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.ToStringMethod; import static net.bytebuddy.matcher.ElementMatchers.named; // 动态生成类 Class<?> dynamicClass = new ByteBuddy() .subclass(Object.class) .name(clazz.getName()) // 添加字段和对应的getter/setter .defineField("name", String.class, java.lang.reflect.Modifier.PRIVATE) .defineMethod("getName", String.class, java.lang.reflect.Modifier.PUBLIC) .intercept(FieldAccessor.ofBeanProperty()) .defineMethod("setName", void.class, java.lang.reflect.Modifier.PUBLIC) .withParameter(String.class) .intercept(FieldAccessor.ofBeanProperty()) // 生成toString方法 .method(named("toString")) .intercept(ToStringMethod.prefixedBy(clazz.getName())) .make() .load(getClass().getClassLoader()) .getLoaded();
Final Notes
Lombok is an amazing tool for static compilation, but it’s not designed to work with runtime-generated classes. Your best bet is to either replicate its functionality manually with Javassist or use a runtime-focused bytecode library.
内容的提问来源于stack exchange,提问作者Jayachandra Ch

