Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

With bytebuddy is it possible to enhance a field that is annotated where the annotation has a specific attribute value?

I'm trying to use bytebuddy to redefine existing classes. I'm looking for fields that are annotated with a specific annotation. I've got that figure out with code something like this:

new ByteBuddy()
        .redefine(<some class>)
        .field(
            ElementMatchers.isAnnotatedWith(<some annotation>)
        )
        ...

What I'd like to do is further refine my ElementMatcher to include a check for an attribute on the specified annotation - something like this:

new ByteBuddy()
        .redefine(<some class>)
        .field(
            ElementMatchers.isAnnotatedWith(<some annotation>)
                .havingAttribute(<some attribute>, "value")
        )

What I'm looking for is the way to do the "havingAttribute" part. Is this possible or am I approaching this the wrong way? Any insight is appreciated.

like image 931
Jay Paulsen Avatar asked Jun 04 '26 18:06

Jay Paulsen


2 Answers

One approach would be to create a Advice.OffsetMapping.Factory which lets you inject the annotation value in your advice like this:

@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onMethodEnter(@AnnotationValueExtractor(annotationClassName = "co.elastic.apm.api.CaptureSpan", method = "value") String spanName) {
    if (spanName.equals("foo")) {
        // do something special
    }
}

See also: https://github.com/elastic/apm-agent-java/blob/f6781c3d740602f000332f9b4a5b5ecb0d01627a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/AnnotationValueOffsetMappingFactory.java

like image 192
Felix Avatar answered Jun 07 '26 07:06

Felix


I ended up implementing a custom ElementMatcher like this:

public class NeedsLazyToOneNoProxy<T extends AnnotationSource> extends ElementMatcher.Junction.AbstractBase<T> {


    public boolean matches(T target) {
        AnnotationDescription oneToOneAnnotation = target.getDeclaredAnnotations().ofType(OneToOne.class);
        try {
            if (oneToOneAnnotation != null) {
                OneToOne oneToOne = (OneToOne) ((AnnotationDescription.Loadable) oneToOneAnnotation).load();
                FetchType fetchType = oneToOne.fetch();
                return fetchType == FetchType.LAZY;
            }
            return false;
        }
        catch (ClassNotFoundException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

    }
}

I use this element matcher to determine if I should add a @LazyToOne annotation to an existing @OneToOne relationship.

like image 37
Jay Paulsen Avatar answered Jun 07 '26 06:06

Jay Paulsen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!