raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.29k stars 807 forks source link

How to clone an annotation and change it's property value #1527

Closed MrLiuzy closed 1 year ago

MrLiuzy commented 1 year ago

I want clone an annotation, change some properties of the clone annotation, and then add the clone annotation to another class. how can I do ?

@ApiModel("user information") 
public class User {
    @ApiModelProperty("username")
    private String name;
    @ApiModelProperty("age")
    private int age;
}
                // the raw's value is "user information";
        ApiModel raw = User.class.getAnnotation(ApiModel.class);

        AnnotationValue<?, ?> asValue = AnnotationDescription.ForLoadedAnnotation.asValue(raw, ApiModel.class);
        // how can change the clone's value to "other value"
        ApiModel clone = asValue.resolve(AnnotationDescription.class).prepare(ApiModel.class).load();

        Class<?> loaded = new ByteBuddy()
            .redefine(User.class)
            .name("User1")
            .annotateType(clone)
            .make()
            .load(getClass().getClassLoader())
            .getLoaded();
raphw commented 1 year ago

This would require you to use ASM by registering a visitor. There is no good support for this otherwise as things are today, unfortunately. You can register one using the visit method. See the ASM doc for more reference.

MrLiuzy commented 1 year ago

Hi raphw! Thanks for your reply! After a try. I found a way to resolve my problem from the AnnotationDescription.AnnotationInvocationHandler

<S extends Annotation> S net.bytebuddy.description.annotation.AnnotationDescription.AnnotationInvocationHandler.of(@MaybeNull ClassLoader classLoader, Class<S> annotationType, Map<String, ? extends AnnotationValue<?, ?>> values)

like this

    public static <T extends Annotation>T copy(T raw, Map<String, Object> replacement) {
        Map<String, Object> valueMap = Collections.unmodifiableMap(replacement);
        InvocationHandler handler = (proxy, method, args)->{
            Object object = valueMap.get(method.getName());
            if(object!=null) {
                return object;
            }
            return method.invoke(raw, args);
        };
        return (T) Proxy.newProxyInstance(raw.getClass().getClassLoader(), new Class<?>[] {raw.annotationType()}, handler);
    }

By the way, can you give me a example that how to use the ASM visitor to change or add or delete the annotation? Thank you very mach!

raphw commented 1 year ago

Please see the ASM documentation which showcases such use. For removing, you simply return null. You can add a new annotation by visiting with a new visitor. The third option is to modify properties within the visitor.

MrLiuzy commented 1 year ago

Thanks