Assume an annotation that declares a value() attribute explicitly aliasing an attribute other than @Component(value = "…").

@interface MyAnnotation {

  @AliasFor(annotation = Foo.class, attribute = "…")
  String value() default "";
}

If that annotation gets used with a set value (@MyAnnotation("foo")), I now see a warning:

Support for convention-based stereotype names is deprecated and will \
be removed in a future version of the framework. Please annotate the \
'value' attribute in @MyAnnotation with @AliasFor(annotation=Component.class) \
to declare an explicit alias for @Component's 'value' attribute.

This warning should not be issued if the value() attribute is explicitly aliased to some attribute other than @Component's value.

Also, the phrasing of "convention-based stereotype names" in combination with @Component led me to believe it's about the bean name stereotypically being derived from the class name (which made it particularly puzzling as the warning only appears with a value set, which would not trigger that convention). It turns out this term is actually referring to the default aliasing convention of the annotation attributes (the @MyAnnotation.value() conventionally being considered a redeclaration of @Component.value()). I wonder if the message's wording could be adapted to clarify this.

Comment From: sbrannen

Related Issues

  • 28773

  • 31089

  • 28761

Comment From: sbrannen

This raises some interesting questions, so thanks for bringing this to our attention! 👍

I wonder if the message's wording could be adapted to clarify this.

We can definitely improve the wording, but before we do that we should decide what to do about the status quo in terms of actual behavior.

This warning should not be issued if the value attribute is explicitly aliased to some attribute other than @Component's value.

Yes and no. Let me explain...

With the current behavior, we need such a warning, because that value is actually used as the @Component name (see example below).

But if we were to no longer use such an explicitly aliased value as the @Component name, then the message would not be necessary for that particular use case.


I put together the following to demonstrate the status quo.

@Retention(RetentionPolicy.RUNTIME)
@interface SomeAnnotation {

    String attribute() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Component
@SomeAnnotation
@interface MyComponent {

    @AliasFor(annotation = SomeAnnotation.class, attribute = "attribute")
    String value() default "";
}
@MyComponent("enigma")
class ConcreteComponent {
}
@SpringJUnitConfig
class ComponentNameTests {

    @Test
    void annotationValues() {
        MergedAnnotations mergedAnnotations = MergedAnnotations.from(ConcreteComponent.class);
        SomeAnnotation someAnnotation = mergedAnnotations.get(SomeAnnotation.class).synthesize();
        Component component = mergedAnnotations.get(Component.class).synthesize();

        // @Component(value = "")
        assertThat(component.value()).isBlank();
        // @MyComponent(value = "enigma") -> @SomeAnnotation(attribute = "enigma")
        assertThat(someAnnotation.attribute()).isEqualTo("enigma");
    }

    @Test
    void beanName(ApplicationContext context) {
        // "enigma" comes from @MyComponent("enigma");
        // whereas, AnnotationBeanNameGenerator would generate "concreteComponent".
        assertThat(context.getBeanNamesForType(ConcreteComponent.class))
                .containsExactly("enigma");
    }
    @Configuration
    @ComponentScan(includeFilters =
        @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ConcreteComponent.class))
    static class Config {
    }
}

The above tests pass which means that the @Component name for the component-scanned ConcreteComponent bean comes from the @MyComponent("enigma") declaration even though MyComponent.value is an explicit @AliasFor SomeAnnotation.attribute.

Thus, with the status quo, that log message is intended to warn you that MyComponent.value is interpreted as the @Component name even if that is not your intention.

Historically, a String value() attribute has always been considered the @Component name. Since Spring Framework 6.1 (see #31089), one should ideally make use of @AliasFor. However, the value convention remains in effect. We would like to remove that support at some point in the future, but we have not yet decided when we will do that.


Now that we've clarified the status quo, let's look at our options.

  1. Leave the behavior as-is and update the warning to better explain what's going on.
  2. Stop using a value attribute that is explicitly aliased to something other than @Component.value as the @Component name.
  3. Stop supporting the String value() convention for @Component names.

We can do #1 in 6.2.x and backport that to 6.1.x.

We could consider doing #2 in 6.2.x or 7.0.

We could consider doing #3 in 7.0 or 8.0.

Note, however, that #2 and #3 would technically be breaking changes for certain users.

If anyone would like to share their thoughts on the matter, feel free to do so here. In any case, we will discuss this within the Framework team.

Comment From: sbrannen

Update

For Spring Framework 6.2.x and 6.1.x, we will use this GitHub issue to improve the log message to clarify the following different scenarios (effectively option #1 above).

  • value is not annotated with @AliasFor and is therefore used as a convention-based override for Component.value.
  • value is annotated with @AliasFor as an override for an attribute other than Component.value and yet is still used as the @Component name.

For Spring Framework 7 (see #34346), we will stop using a value attribute that is explicitly aliased to something other than @Component.value as the @Component name (effectively option #2 above).

Comment From: sbrannen

  • value is annotated with @AliasFor as an override for an attribute other than Component.value and yet is still used as the @Component name.

In a local POC, the following warning is now generated for the @MyComponent example above.

WARN  o.s.c.a.AnnotationBeanNameGenerator - Although the 'value' attribute \
in @example.MyComponent is annotated with @AliasFor for an attribute other \
than @Component's 'value' attribute, the value is still used as the \
@Component name based on convention. As of Spring Framework 7.0, such a \
'value' attribute will no longer be used as the @Component name.

@odrotbohm and @jhoeller, WDYT?