|
20 | 20 | import java.lang.annotation.Retention;
|
21 | 21 | import java.lang.annotation.RetentionPolicy;
|
22 | 22 | import java.lang.annotation.Target;
|
| 23 | +import java.util.List; |
23 | 24 |
|
24 | 25 | import example.scannable.DefaultNamedComponent;
|
25 | 26 | import example.scannable.JakartaManagedBeanComponent;
|
|
33 | 34 | import org.springframework.beans.factory.config.BeanDefinition;
|
34 | 35 | import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
35 | 36 | import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
|
| 37 | +import org.springframework.core.annotation.AliasFor; |
36 | 38 | import org.springframework.stereotype.Component;
|
37 | 39 | import org.springframework.stereotype.Controller;
|
38 | 40 | import org.springframework.stereotype.Service;
|
@@ -73,20 +75,34 @@ void generateBeanNameWithNamedComponentWhereTheNameIsBlank() {
|
73 | 75 | assertGeneratedNameIsDefault(ComponentWithBlankName.class);
|
74 | 76 | }
|
75 | 77 |
|
| 78 | + @Test |
| 79 | + void generateBeanNameForConventionBasedComponentWithDuplicateIdenticalNames() { |
| 80 | + assertGeneratedName(ConventionBasedComponentWithDuplicateIdenticalNames.class, "myComponent"); |
| 81 | + } |
| 82 | + |
76 | 83 | @Test
|
77 | 84 | void generateBeanNameForComponentWithDuplicateIdenticalNames() {
|
78 | 85 | assertGeneratedName(ComponentWithDuplicateIdenticalNames.class, "myComponent");
|
79 | 86 | }
|
80 | 87 |
|
81 | 88 | @Test
|
82 |
| - void generateBeanNameForComponentWithConflictingNames() { |
83 |
| - BeanDefinition bd = annotatedBeanDef(ComponentWithMultipleConflictingNames.class); |
| 89 | + void generateBeanNameForConventionBasedComponentWithConflictingNames() { |
| 90 | + BeanDefinition bd = annotatedBeanDef(ConventionBasedComponentWithMultipleConflictingNames.class); |
84 | 91 | assertThatIllegalStateException()
|
85 | 92 | .isThrownBy(() -> generateBeanName(bd))
|
86 | 93 | .withMessage("Stereotype annotations suggest inconsistent component names: '%s' versus '%s'",
|
87 | 94 | "myComponent", "myService");
|
88 | 95 | }
|
89 | 96 |
|
| 97 | + @Test |
| 98 | + void generateBeanNameForComponentWithConflictingNames() { |
| 99 | + BeanDefinition bd = annotatedBeanDef(ComponentWithMultipleConflictingNames.class); |
| 100 | + assertThatIllegalStateException() |
| 101 | + .isThrownBy(() -> generateBeanName(bd)) |
| 102 | + .withMessage("Stereotype annotations suggest inconsistent component names: " + |
| 103 | + List.of("myComponent", "myService")); |
| 104 | + } |
| 105 | + |
90 | 106 | @Test
|
91 | 107 | void generateBeanNameWithJakartaNamedComponent() {
|
92 | 108 | assertGeneratedName(JakartaNamedComponent.class, "myJakartaNamedComponent");
|
@@ -142,6 +158,16 @@ void generateBeanNameFromComposedControllerAnnotationWithStringValue() {
|
142 | 158 | assertGeneratedName(ComposedControllerAnnotationWithStringValue.class, "restController");
|
143 | 159 | }
|
144 | 160 |
|
| 161 | + @Test // gh-31089 |
| 162 | + void generateBeanNameFromStereotypeAnnotationWithStringArrayValueAndExplicitComponentNameAlias() { |
| 163 | + assertGeneratedName(ControllerAdviceClass.class, "myControllerAdvice"); |
| 164 | + } |
| 165 | + |
| 166 | + @Test // gh-31089 |
| 167 | + void generateBeanNameFromSubStereotypeAnnotationWithStringArrayValueAndExplicitComponentNameAlias() { |
| 168 | + assertGeneratedName(RestControllerAdviceClass.class, "myRestControllerAdvice"); |
| 169 | + } |
| 170 | + |
145 | 171 |
|
146 | 172 | private void assertGeneratedName(Class<?> clazz, String expectedName) {
|
147 | 173 | BeanDefinition bd = annotatedBeanDef(clazz);
|
@@ -181,6 +207,28 @@ static class ComponentWithDuplicateIdenticalNames {
|
181 | 207 | static class ComponentWithMultipleConflictingNames {
|
182 | 208 | }
|
183 | 209 |
|
| 210 | + @Retention(RetentionPolicy.RUNTIME) |
| 211 | + @Component |
| 212 | + @interface ConventionBasedComponent1 { |
| 213 | + String value() default ""; |
| 214 | + } |
| 215 | + |
| 216 | + @Retention(RetentionPolicy.RUNTIME) |
| 217 | + @Component |
| 218 | + @interface ConventionBasedComponent2 { |
| 219 | + String value() default ""; |
| 220 | + } |
| 221 | + |
| 222 | + @ConventionBasedComponent1("myComponent") |
| 223 | + @ConventionBasedComponent2("myComponent") |
| 224 | + static class ConventionBasedComponentWithDuplicateIdenticalNames { |
| 225 | + } |
| 226 | + |
| 227 | + @ConventionBasedComponent1("myComponent") |
| 228 | + @ConventionBasedComponent2("myService") |
| 229 | + static class ConventionBasedComponentWithMultipleConflictingNames { |
| 230 | + } |
| 231 | + |
184 | 232 | @Component
|
185 | 233 | private static class AnonymousComponent {
|
186 | 234 | }
|
@@ -224,4 +272,55 @@ static class ComposedControllerAnnotationWithBlankName {
|
224 | 272 | static class ComposedControllerAnnotationWithStringValue {
|
225 | 273 | }
|
226 | 274 |
|
| 275 | + /** |
| 276 | + * Mock of {@code org.springframework.web.bind.annotation.ControllerAdvice}, |
| 277 | + * which also has a {@code value} attribute that is NOT a {@code String} that |
| 278 | + * is meant to be used for the component name. |
| 279 | + * <p>Declares a custom {@link #name} that explicitly aliases {@link Component#value()}. |
| 280 | + */ |
| 281 | + @Retention(RetentionPolicy.RUNTIME) |
| 282 | + @Target(ElementType.TYPE) |
| 283 | + @Component |
| 284 | + @interface TestControllerAdvice { |
| 285 | + |
| 286 | + @AliasFor(annotation = Component.class, attribute = "value") |
| 287 | + String name() default ""; |
| 288 | + |
| 289 | + @AliasFor("basePackages") |
| 290 | + String[] value() default {}; |
| 291 | + |
| 292 | + @AliasFor("value") |
| 293 | + String[] basePackages() default {}; |
| 294 | + } |
| 295 | + |
| 296 | + /** |
| 297 | + * Mock of {@code org.springframework.web.bind.annotation.RestControllerAdvice}, |
| 298 | + * which also has a {@code value} attribute that is NOT a {@code String} that |
| 299 | + * is meant to be used for the component name. |
| 300 | + * <p>Declares a custom {@link #name} that explicitly aliases |
| 301 | + * {@link TestControllerAdvice#name()} instead of {@link Component#value()}. |
| 302 | + */ |
| 303 | + @Retention(RetentionPolicy.RUNTIME) |
| 304 | + @TestControllerAdvice |
| 305 | + @interface TestRestControllerAdvice { |
| 306 | + |
| 307 | + @AliasFor(annotation = TestControllerAdvice.class) |
| 308 | + String name() default ""; |
| 309 | + |
| 310 | + @AliasFor(annotation = TestControllerAdvice.class) |
| 311 | + String[] value() default {}; |
| 312 | + |
| 313 | + @AliasFor(annotation = TestControllerAdvice.class) |
| 314 | + String[] basePackages() default {}; |
| 315 | + } |
| 316 | + |
| 317 | + |
| 318 | + @TestControllerAdvice(basePackages = "com.example", name = "myControllerAdvice") |
| 319 | + static class ControllerAdviceClass { |
| 320 | + } |
| 321 | + |
| 322 | + @TestRestControllerAdvice(basePackages = "com.example", name = "myRestControllerAdvice") |
| 323 | + static class RestControllerAdviceClass { |
| 324 | + } |
| 325 | + |
227 | 326 | }
|
0 commit comments