Skip to content
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

Spring does not find all @ComponentScan or @PropertySource annotations #30941

Closed
ladzar opened this issue Jul 25, 2023 · 8 comments
Closed

Spring does not find all @ComponentScan or @PropertySource annotations #30941

ladzar opened this issue Jul 25, 2023 · 8 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Milestone

Comments

@ladzar
Copy link

ladzar commented Jul 25, 2023

Affects: 6.0.11


When I compose my own annotations which all use @ComponentScan and I use those annotations on an application class, only the first one is used:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@ComponentScan(basePackageClasses = ModuleAClient.class)
public @interface ConnectedToModuleA {
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@ComponentScan(basePackageClasses = ModuleBClient.class)
public @interface ConnectedToModuleB {
}
@ConnectedToModuleA // Only the ComponentScan from this annotation is picked
@ConnectedToModuleB // ComponentScan from this annotation is ignored
public class MyApplication {

	public static void main(String[] args) {
		SpringApplication.run(MyApplication.class, args);
	}
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jul 25, 2023
@snicoll
Copy link
Member

snicoll commented Jul 27, 2023

Thanks for the report. This looks like a bug to me, and I think org.springframework.context.annotation.AnnotationConfigUtils#attributesForRepeatable shouldn't call metadata.getAnnotationAttributes but rather metadata.getAllAnnotationAttributes(ComponentScan.class.getName()).

Edit: however doing so does not manage overrides so I am not sure if that's such a good idea.

@jhoeller, thoughts?

@snicoll snicoll added type: bug A general bug in: core Issues in core modules (aop, beans, core, context, expression) and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jul 27, 2023
@snicoll snicoll added this to the 6.1.x milestone Jul 27, 2023
@sbrannen sbrannen changed the title ComponentScan not picked when present on multiple custom annotations and used on the same class @ComponentScan not picked when present on multiple composed annotations on the same class Aug 6, 2023
@sbrannen
Copy link
Member

sbrannen commented Aug 6, 2023

however doing so does not manage overrides so I am not sure if that's such a good idea.

Right. We cannot switch to getAllAnnotationAttributes as-is, since that would break numerous existing use cases.

However, we might be able to introduce a variant of getAllAnnotationAttributes that omits the .map(MergedAnnotation::withNonMergedAttributes) restriction.

If I recall correctly, that restriction was included in order to match the behavior when using the MergedAnnotation API with the previous behavior, and prior to the MergedAnnotation API we did not have a way to merge all annotation attributes when using ASM.

Of course, simply removing .map(MergedAnnotation::withNonMergedAttributes) and processing the resulting MultiValueMap may not work as needed, but it's worth investigating.

@sbrannen sbrannen self-assigned this Aug 6, 2023
@sbrannen
Copy link
Member

sbrannen commented Aug 6, 2023

I've pushed a "proof of concept" solution to the following feature branch.

main...sbrannen:spring-framework:issues/gh-30941-repeatable-composed-ComponentScan-annotations

It introduces two default getAllMergedAnnotationAttributes(...) methods in AnnotatedTypeMetadata, which allow the use case in this issue to pass.

In addition, the rest of the test suite passes; however, if we decide to introduce these methods we will of course need to introduce additional tests.

@jhoeller and @snicoll, thoughts?

@sbrannen sbrannen changed the title @ComponentScan not picked when present on multiple composed annotations on the same class @ComponentScan not picked when present on multiple composed annotations Aug 6, 2023
@sbrannen
Copy link
Member

sbrannen commented Aug 6, 2023

We need to keep in mind that @PropertySource is also affected by this.

If we make the changes I proposed above, the following Javadoc for @PropertySource may no longer be applicable.

... all such @PropertySource annotations need to be declared at the same level: either directly on the configuration class or as meta-annotations on the same custom annotation. Mixing direct annotations and meta-annotations is not recommended since direct annotations will effectively override meta-annotations.

@sbrannen sbrannen changed the title @ComponentScan not picked when present on multiple composed annotations Only first @ComponentScan or @PropertySource composed annotation is discovered Aug 7, 2023
@sbrannen
Copy link
Member

sbrannen commented Aug 7, 2023

We need to keep in mind that @PropertySource is also affected by this.

In light of that, I've updated the title of this issue and pushed related tests to the aforementioned feature branch.

@sbrannen sbrannen modified the milestones: 6.1.x, 6.1.0-M4 Aug 7, 2023
@sbrannen sbrannen changed the title Only first @ComponentScan or @PropertySource composed annotation is discovered Spring does not find all @ComponentScan or @PropertySource annotations Aug 12, 2023
@sbrannen
Copy link
Member

After further analysis, it turns out that in addition to not finding multiple composed @ComponentScan or @PropertySource annotations, Spring also does not find multiple repeated @ComponentScan or @PropertySource annotations within an annotation hierarchy.

Both of these bugs stem from the fact that AnnotationConfigUtils.attributesForRepeatable(...) invokes AnnotatedTypeMetadata.getAnnotationAttributes(...) for both direct annotations and annotations in a container, and getAnnotationAttributes(...) only finds the first such annotation.

In light of that, I have revised the title of this issue and opened #31041 to introduce explicit support for finding all repeatable annotations via the AnnotatedTypeMetadata abstraction.

sbrannen added a commit to sbrannen/spring-framework that referenced this issue Aug 12, 2023
sbrannen added a commit to sbrannen/spring-framework that referenced this issue Aug 12, 2023
sbrannen added a commit to sbrannen/spring-framework that referenced this issue Aug 12, 2023
sbrannen added a commit that referenced this issue Aug 12, 2023
AnnotatedTypeMetadata has various methods for finding annotations;
however, prior to this commit it did not provide explicit support for
repeatable annotations.

Although it is possible to craft a search "query" for repeatable
annotations using the MergedAnnotations API via getAnnotations(), that
requires intimate knowledge of the MergedAnnotations API as well as the
structure of repeatable annotations.

Furthermore, the bugs reported in gh-30941 result from the fact that
AnnotationConfigUtils attempts to use the existing functionality in
AnnotatedTypeMetadata to find repeatable annotations without success.

This commit introduces a getMergedRepeatableAnnotationAttributes()
method in AnnotatedTypeMetadata that provides dedicated support for
finding merged repeatable annotation attributes with full @AliasFor
semantics.

Closes gh-31041
@ladzar
Copy link
Author

ladzar commented Aug 13, 2023

Thanks very much for taking the time to fix this issue.

@sbrannen
Copy link
Member

Thanks very much for taking the time to fix this issue.

You're welcome!

sbrannen added a commit that referenced this issue Dec 6, 2023
Work performed in conjunction with gh-30941 resulted in a regression.
Specifically, prior to Spring Framework 6.1 a locally declared
@⁠ComponentScan annotation took precedence over @⁠ComponentScan
meta-annotations, which allowed "local" configuration to override
"meta-present" configuration.

This commit modifies the @⁠ComponentScan search algorithm so that
locally declared @⁠ComponentScan annotations are once again favored
over @⁠ComponentScan meta-annotations (and, indirectly, composed
annotations).

See gh-30941 Closes gh-31704
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants