Ensure synthesized nested annotation arrays retain correct type
Prior to this commit, when a nested array of annotations was synthesized while adapting values within an AnnotationAttributes map, the array was improperly replaced with an array of type Annotation[] instead of an array of the concrete annotation type, which can lead to unexpected run-time exceptions. This commit fixes this bug by replacing annotations in the existing array with synthesized versions of those annotations, thereby retaining the original array's component type. Issue: SPR-13077
This commit is contained in:
parent
a2f152ce8b
commit
f41de12cf6
|
|
@ -924,11 +924,10 @@ public abstract class AnnotationUtils {
|
|||
return mappedAnnotations;
|
||||
}
|
||||
else {
|
||||
Annotation[] synthesizedAnnotations = new Annotation[annotations.length];
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
synthesizedAnnotations[i] = synthesizeAnnotation(annotations[i], annotatedElement);
|
||||
annotations[i] = synthesizeAnnotation(annotations[i], annotatedElement);
|
||||
}
|
||||
return synthesizedAnnotations;
|
||||
return annotations;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
|
@ -32,9 +32,9 @@ import org.junit.rules.ExpectedException;
|
|||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.stream.Collectors.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.annotation.AnnotatedElementUtils.*;
|
||||
|
||||
|
|
@ -460,8 +460,23 @@ public class AnnotatedElementUtilsTests {
|
|||
attributes.getString("qualifier"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAnnotationAttributesOnClassWithAttributeAliasInComposedAnnotationAndNestedAnnotationsInTargetAnnotation() {
|
||||
Class<?> element = TestComponentScanClass.class;
|
||||
AnnotationAttributes attributes = findAnnotationAttributes(element, ComponentScan.class);
|
||||
assertNotNull("Should find @ComponentScan on " + element, attributes);
|
||||
assertArrayEquals("basePackages for " + element, new String[] { "com.example.app.test" },
|
||||
attributes.getStringArray("basePackages"));
|
||||
|
||||
Filter[] excludeFilters = attributes.getAnnotationArray("excludeFilters", Filter.class);
|
||||
assertNotNull(excludeFilters);
|
||||
|
||||
List<String> patterns = stream(excludeFilters).map(Filter::pattern).collect(toList());
|
||||
assertEquals(asList("*Test", "*Tests"), patterns);
|
||||
}
|
||||
|
||||
private Set<String> names(Class<?>... classes) {
|
||||
return stream(classes).map(clazz -> clazz.getName()).collect(Collectors.toSet());
|
||||
return stream(classes).map(Class::getName).collect(toSet());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -627,6 +642,32 @@ public class AnnotatedElementUtilsTests {
|
|||
String[] xmlConfigFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock of {@code org.springframework.context.annotation.ComponentScan}
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ComponentScan {
|
||||
|
||||
String[] basePackages() default {};
|
||||
|
||||
Filter[] excludeFilters() default {};
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({})
|
||||
@interface Filter {
|
||||
|
||||
String pattern();
|
||||
}
|
||||
|
||||
@ComponentScan(excludeFilters = { @Filter(pattern = "*Test"), @Filter(pattern = "*Tests") })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface TestComponentScan {
|
||||
|
||||
@AliasFor(attribute = "basePackages", annotation = ComponentScan.class)
|
||||
String[] packages();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class NonAnnotatedClass {
|
||||
|
|
@ -776,4 +817,9 @@ public class AnnotatedElementUtilsTests {
|
|||
@InvalidAliasedComposedContextConfig(xmlConfigFiles = "test.xml")
|
||||
static class InvalidAliasedComposedContextConfigClass {
|
||||
}
|
||||
|
||||
@TestComponentScan(packages = "com.example.app.test")
|
||||
static class TestComponentScanClass {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import java.lang.annotation.Inherited;
|
|||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
|
@ -35,6 +36,7 @@ import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass;
|
|||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.stream.Collectors.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
|
@ -393,6 +395,22 @@ public class AnnotationUtilsTests {
|
|||
assertEquals(Component.class, attributes.annotationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnotationAttributesWithNestedAnnotations() {
|
||||
ComponentScan componentScan = ComponentScanClass.class.getAnnotation(ComponentScan.class);
|
||||
assertNotNull(componentScan);
|
||||
|
||||
AnnotationAttributes attributes = getAnnotationAttributes(ComponentScanClass.class, componentScan);
|
||||
assertNotNull(attributes);
|
||||
assertEquals(ComponentScan.class, attributes.annotationType());
|
||||
|
||||
Filter[] filters = attributes.getAnnotationArray("excludeFilters", Filter.class);
|
||||
assertNotNull(filters);
|
||||
|
||||
List<String> patterns = stream(filters).map(Filter::pattern).collect(toList());
|
||||
assertEquals(asList("*Foo", "*Bar"), patterns);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnotationAttributesWithAttributeAliases() throws Exception {
|
||||
Method method = WebController.class.getMethod("handleMappedWithValueAttribute");
|
||||
|
|
@ -1222,4 +1240,22 @@ public class AnnotationUtilsTests {
|
|||
String xmlConfigFile();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({})
|
||||
@interface Filter {
|
||||
String pattern();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock of {@code org.springframework.context.annotation.ComponentScan}
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ComponentScan {
|
||||
Filter[] excludeFilters() default {};
|
||||
}
|
||||
|
||||
@ComponentScan(excludeFilters = { @Filter(pattern = "*Foo"), @Filter(pattern = "*Bar") })
|
||||
static class ComponentScanClass {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue