-
Notifications
You must be signed in to change notification settings - Fork 298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
methods().that().areAnnotatedWith() does not match methods which have multiple instances of the same annotation type #419
Comments
This behavior is due to the fact of how Java handles repeatable annotations. In your example, @Retention(RetentionPolicy.RUNTIME)
@Repeatable(ApiResponses.class)
public @interface ApiResponse {
int status();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiResponses {
ApiResponse[] value();
} Also, consider these two example usages: @ApiResponse(status = 200)
public class FirstAnnotatedClass {
}
@ApiResponse(status = 200)
@ApiResponse(status = 404)
public class SecondAnnotatedClass {
} If we use Java's Reflection API to get the annotations, we get the following result: System.out.println(FirstAnnotatedClass.class.getAnnotation(ApiResponse.class)); // prints @org.example.ApiResponse(status=200)
System.out.println(SecondAnnotatedClass.class.getAnnotation(ApiResponse.class)); // prints null What happened? Somewhere between your code and the reflective call, the repeatable annotation is wrapped into the containing annotation type, but only if the annotation is repeated. (I'm not sure if this happens during compile time or during runtime, but it doesn't matter.) If we try to read the containing annotation type with Java's Reflection API, we get: System.out.println(FirstAnnotatedClass.class.getAnnotation(ApiResponses.class)); // prints null
System.out.println(SecondAnnotatedClass.class.getAnnotation(ApiResponses.class)); // @org.example.ApiResponses(value=[@org.example.ApiResponse(status=200), @org.example.ApiResponse(status=404)]) And now compared to the behavior of ArchUnit: JavaClasses classes = new ClassFileImporter().importClasses(FirstAnnotatedClass.class, SecondAnnotatedClass.class);
System.out.println(classes.get(FirstAnnotatedClass.class).tryGetAnnotationOfType(ApiResponse.class).orNull()); // prints @org.example.ApiResponse(status=200)
System.out.println(classes.get(SecondAnnotatedClass.class).tryGetAnnotationOfType(ApiResponse.class).orNull()); // prints null
System.out.println(classes.get(FirstAnnotatedClass.class).tryGetAnnotationOfType(ApiResponses.class).orNull()); // prints null
System.out.println(classes.get(SecondAnnotatedClass.class).tryGetAnnotationOfType(ApiResponses.class).orNull()); // @org.example.ApiResponses(value=[@org.example.ApiResponse(status=200), @org.example.ApiResponse(status=404)]) Compared to Java's Reflection API, ArchUnit produces the same results. So I would say this is the intended behavior. My summary is, if you have to deal with repeatable annotations, you have to check both the annotation type and the containing annotation type. |
ArchUnit mirrors the bytecode, so that should answer the question 😉 Other than that, yes, ArchUnit tries to mirror the Reflection API to avoid confusion (and duplicate confusion if the Reflection API is confusing 😂 ) |
Thank you for the quick answer, helps a lot 🙂 |
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3.6.0 to 3.8.0. from [actions/setup-java's releases](https://github.com/actions/setup-java/releases): > # v3.8.0 > > In scope of this release we added logic to pass the token input through on GHES for Microsoft Build of OpenJDK ([actions/setup-java#395](https://github-redirect.dependabot.com/actions/setup-java/pull/395)) and updated [minimatch](https://github-redirect.dependabot.com/actions/setup-java/pull/413) dependency. Commits * [`c3ac5dd`](actions/setup-java@c3ac5dd) Revert "Add support for Oracle JDK ([#401](https://github-redirect.dependabot.com/actions/setup-java/issues/401))" ([#421](https://github-redirect.dependabot.com/actions/setup-java/issues/421)) * [`dcd29da`](actions/setup-java@dcd29da) Fix typo in README.md ([#419](https://github-redirect.dependabot.com/actions/setup-java/issues/419)) * [`19eeec5`](actions/setup-java@19eeec5) Update to latest `actions/publish-action` ([#411](https://github-redirect.dependabot.com/actions/setup-java/issues/411)) * [`bd7e5d2`](actions/setup-java@bd7e5d2) Update minimatch to 3.1.2 ([#413](https://github-redirect.dependabot.com/actions/setup-java/issues/413)) * [`6cdf39a`](actions/setup-java@6cdf39a) Add support for Oracle JDK ([#401](https://github-redirect.dependabot.com/actions/setup-java/issues/401)) * [`7db6b45`](actions/setup-java@7db6b45) Eclipse Temurin instead of Adopt OpenJDK ([#398](https://github-redirect.dependabot.com/actions/setup-java/issues/398)) * [`bf2f02c`](actions/setup-java@bf2f02c) Pass the token input through on GHES for Microsoft Build of OpenJDK ([#395](https://github-redirect.dependabot.com/actions/setup-java/issues/395)) * See full diff in [compare view](actions/setup-java@v3.6.0...v3.8.0)
I wanted to write up a little workaround that seemed to work for me if anyone else finds themselves in this situation. Instead of using And pass in a custom ArchCondition which does something like the following in this case checking OP's
|
Hello,
I don't know if this is due to me misconfiguring something, or due to ArchUnit, but I have a case where areAnnotatedWith() is not finding all the instances in a class.
In the following arch test:
Not all methods get matched:
I figured that that was because putImage() has multiple
@ApiResponse
annotations, but I'm not sure why that happens and what can I do to avoid it.Is this intended ArchUnit behaviour or am I missing something?
Sorry if this question was asked before, but I found nothing on the topic.
The text was updated successfully, but these errors were encountered: