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

Improve exception message for mixed explicit and implicit aliases with @AliasFor #24168

Closed
BartoszCoyote opened this issue Dec 9, 2019 · 4 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@BartoszCoyote
Copy link

BartoszCoyote commented Dec 9, 2019

I just upgraded Spring Boot version to 2.2.2 from 2.1.8 and noticed a weird exception:

Caused by: org.springframework.core.annotation.AnnotationConfigurationException: Attribute 'value' in annotation [en.example.package.PostApi] must be declared as an @AliasFor 'path', not 'path'.

Whole code for my custom annotation.

public @interface PostApi {

    @AliasFor(annotation = ResponseStatus.class, attribute = "code") 
    HttpStatus status() default HttpStatus.OK;

    @AliasFor("value") 
    String[] path() default {};

    @AliasFor(annotation = RequestMapping.class, attribute = "path") 
    String[] value() default {};

    @AliasFor(annotation = RequestMapping.class, attribute = "consumes") 
    String[] consumes() default { MediaType.APPLICATION_JSON_VALUE };

}

Any ideas what is going on?

@snicoll snicoll transferred this issue from spring-projects/spring-boot Dec 9, 2019
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 9, 2019
@sbrannen
Copy link
Member

sbrannen commented Dec 9, 2019

Thanks for raising the issue. We'll take a look.

@sbrannen
Copy link
Member

sbrannen commented Dec 9, 2019

I've been able to reproduce this on master with the following test case.

class AnnotationAttributeOverrideTests {

	@Test
	void test() throws Exception {
		Method method = getClass().getDeclaredMethod("handlerMethod");
		assertThat(AnnotatedElementUtils.findMergedAnnotation(method, PostApi.class)).isNotNull();
	}

	@PostApi
	void handlerMethod() {
	}

	@Retention(RetentionPolicy.RUNTIME)
	@RequestMapping
	@interface PostApi {

		@AliasFor("value")
		String[] path() default {};

		@AliasFor(annotation = RequestMapping.class, attribute = "path")
		String[] value() default {};
	}

}

@sbrannen
Copy link
Member

sbrannen commented Dec 9, 2019

In the interim, the following works.

@Retention(RetentionPolicy.RUNTIME)
@RequestMapping
@interface PostApi {

	@AliasFor(annotation = RequestMapping.class, attribute = "value")
	String[] path() default {};

	@AliasFor(annotation = RequestMapping.class, attribute = "path")
	String[] value() default {};
}

@sbrannen sbrannen added in: core Issues in core modules (aop, beans, core, context, expression) type: regression A bug that is also a regression and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Dec 9, 2019
@sbrannen sbrannen changed the title AnnotationConfigurationException for attribute in annotation Regression: @AliasFor failure with combination of local and overridden attribute aliases Dec 9, 2019
@sbrannen sbrannen added this to the 5.2.3 milestone Dec 9, 2019
@sbrannen sbrannen changed the title Regression: @AliasFor failure with combination of local and overridden attribute aliases @AliasFor failure with combination of local and overridden attribute aliases Dec 10, 2019
@sbrannen sbrannen removed the type: regression A bug that is also a regression label Dec 10, 2019
@sbrannen
Copy link
Member

sbrannen commented Dec 10, 2019

After further investigation, I have determined that the provided example was only permitted due to a bug that existed prior to Spring Framework 5.2.

In other words, the example provided should never have worked since it combines "explicit" and "implicit" aliasing, which is not supported with @AliasFor.

From the Javadoc for @AliasFor, we see the following Implementation Requirements.

Implicit aliases within an annotation:

  1. Each attribute that belongs to a set of implicit aliases must be annotated with @AliasFor, and attribute must reference the same attribute in the same meta-annotation (either directly or transitively via other explicit meta-annotation attribute overrides within the annotation hierarchy).
  2. Aliased attributes must declare the same return type.
  3. Aliased attributes must declare a default value.
  4. Aliased attributes must declare the same default value.
  5. annotation must reference an appropriate meta-annotation.
  6. The referenced meta-annotation must be meta-present on the annotation class that declares @AliasFor.

Thus, the proper implementation of the @PostApi is as follows.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PostApi {

    @AliasFor(annotation = ResponseStatus.class, attribute = "code") 
    HttpStatus status() default HttpStatus.OK;

    @AliasFor(annotation = RequestMapping.class) 
    String[] value() default {};

    @AliasFor(annotation = RequestMapping.class) 
    String[] path() default {};

    @AliasFor(annotation = RequestMapping.class) 
    String[] consumes() default MediaType.APPLICATION_JSON_VALUE;

}

The above follows the same @AliasFor usage pattern seen in annotations such as @GetMapping, @PostMapping, etc.

Please note as well that the following also results in an exception prior to Spring Framework 5.2.

@Retention(RetentionPolicy.RUNTIME)
@RequestMapping
@interface PostApi {

	@AliasFor("value")
	String[] path() default {};

	@AliasFor(annotation = RequestMapping.class, attribute = "value")
	String[] value() default {};
}

In other words, you simply got lucky that Spring did not throw an exception for your use case, since it was a configuration error.

However, the exception message is indeed a bit confusing.

In light of the above, I am changing the focus of this issue to improved wording of the exception message for 5.2.x; however, we will not make any changes in this regard for previous versions of the Spring Framework.

@sbrannen sbrannen added the type: enhancement A general enhancement label Dec 10, 2019
@sbrannen sbrannen changed the title @AliasFor failure with combination of local and overridden attribute aliases Improve exception message for mixed explicit and implicit aliases with @AliasFor Dec 10, 2019
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: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants