From bbd7fb3969b7fd93c814380209d8f28e2a9c6714 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 11 Nov 2009 19:11:41 +0000 Subject: [PATCH] AnnotationMetadata returns Class values by default (again), allowing for explicit retrieval of String class names where preferred (SPR-5827) --- .../annotation/ConfigurationClass.java | 9 +++-- ...onfigurationClassBeanDefinitionReader.java | 35 ++++++++----------- .../annotation/ConfigurationClassParser.java | 16 +++++---- .../annotation/configuration/ImportTests.java | 11 ++++++ .../core/annotation/AnnotationUtils.java | 6 ++-- .../core/type/AnnotationMetadata.java | 14 ++++++++ .../core/type/StandardAnnotationMetadata.java | 8 +++-- .../AnnotationAttributesReadingVisitor.java | 9 +---- .../AnnotationMetadataReadingVisitor.java | 33 ++++++++++++++++- 9 files changed, 96 insertions(+), 45 deletions(-) diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java index 150c1958148..34b14c000cb 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java @@ -24,7 +24,6 @@ import java.util.Set; import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; -import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.core.io.DescriptiveResource; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; @@ -51,7 +50,7 @@ final class ConfigurationClass { private String beanName; - private final Map importedResources = new LinkedHashMap(); + private final Map importedResources = new LinkedHashMap(); private final Set methods = new LinkedHashSet(); @@ -108,11 +107,11 @@ final class ConfigurationClass { return this.methods; } - public void addImportedResource(String importedResource, String readerClassName) { - this.importedResources.put(importedResource, readerClassName); + public void addImportedResource(String importedResource, Class readerClass) { + this.importedResources.put(importedResource, readerClass); } - public Map getImportedResources() { + public Map getImportedResources() { return this.importedResources; } diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index afc836fa196..ca3c5b78968 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -26,6 +26,7 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor; @@ -41,8 +42,6 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.MethodMetadata; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** @@ -214,28 +213,24 @@ class ConfigurationClassBeanDefinitionReader { registry.registerBeanDefinition(beanName, beanDefToRegister); } - private void loadBeanDefinitionsFromImportedResources(Map importedResources) { - - HashMap readerInstanceCache = new HashMap(); - - for (String resource : importedResources.keySet()) { - String readerClassName = importedResources.get(resource); - - if (!readerInstanceCache.containsKey(readerClassName)) { + private void loadBeanDefinitionsFromImportedResources(Map importedResources) { + Map readerInstanceCache = new HashMap(); + for (Map.Entry entry : importedResources.entrySet()) { + String resource = entry.getKey(); + Class readerClass = entry.getValue(); + if (!readerInstanceCache.containsKey(readerClass)) { try { - @SuppressWarnings("unchecked") - Class readerClass = - (Class) ClassUtils.forName(readerClassName, ClassUtils.getDefaultClassLoader()); - BeanDefinitionReader readerInstance = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry); - readerInstanceCache.put(readerClassName, readerInstance); - } catch (Exception ex) { - ReflectionUtils.handleReflectionException(ex); + BeanDefinitionReader readerInstance = (BeanDefinitionReader) + readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry); + readerInstanceCache.put(readerClass, readerInstance); + } + catch (Exception ex) { + throw new IllegalStateException("Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]"); } } - - BeanDefinitionReader reader = readerInstanceCache.get(readerClassName); + BeanDefinitionReader reader = readerInstanceCache.get(readerClass); // TODO SPR-6310: qualify relatively pathed locations as done in AbstractContextLoader.modifyLocations - reader.loadBeanDefinitions(importedResources.keySet().toArray(new String[]{})); + reader.loadBeanDefinitions(resource); } } diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 179695c0523..684e13e3f46 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -28,14 +28,13 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; -import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.StandardAnnotationMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; /** * Parses a {@link Configuration} class definition, populating a model (collection) of @@ -128,12 +127,17 @@ class ConfigurationClassParser { protected void doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException { if (metadata.isAnnotated(Import.class.getName())) { - processImport(configClass, (String[]) metadata.getAnnotationAttributes(Import.class.getName()).get("value")); + processImport(configClass, (String[]) metadata.getAnnotationAttributes(Import.class.getName(), true).get("value")); } if (metadata.isAnnotated(ImportResource.class.getName())) { - String readerClassName = (String) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("reader"); - for (String importedResource : (String[]) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("value")) { - configClass.addImportedResource(importedResource, readerClassName); + String[] resources = (String[]) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("value"); + Class readerClass = (Class) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("reader"); + if (readerClass == null) { + throw new IllegalStateException("No reader class associated with imported resources: " + + StringUtils.arrayToCommaDelimitedString(resources)); + } + for (String resource : resources) { + configClass.addImportedResource(resource, readerClass); } } Set methods = metadata.getAnnotatedMethods(Bean.class.getName()); diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java index c2115e960dd..8756425e146 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java @@ -60,6 +60,17 @@ public class ImportTests { assertBeanDefinitionCount((configClasses + beansInClasses), ConfigurationWithImportAnnotation.class); } + @Test + public void testProcessImportsWithAsm() { + int configClasses = 2; + int beansInClasses = 2; + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigurationWithImportAnnotation.class.getName())); + ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); + pp.postProcessBeanFactory(beanFactory); + assertThat(beanFactory.getBeanDefinitionCount(), equalTo(configClasses + beansInClasses)); + } + @Test public void testProcessImportsWithDoubleImports() { int configClasses = 3; diff --git a/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index de46417e1af..1c2dec63a71 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -286,20 +286,20 @@ public abstract class AnnotationUtils { /** * Retrieve the given annotation's attributes as a Map. * @param annotation the annotation to retrieve the attributes for - * @param filterClasses whether to turn Class references into Strings + * @param classValuesAsString whether to turn Class references into Strings * (for compatibility with {@link org.springframework.core.type.AnnotationMetadata} * or to preserve them as Class references * @return the Map of annotation attributes, with attribute names as keys * and corresponding attribute values as values */ - public static Map getAnnotationAttributes(Annotation annotation, boolean filterClasses) { + public static Map getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) { Map attrs = new HashMap(); Method[] methods = annotation.annotationType().getDeclaredMethods(); for (Method method : methods) { if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) { try { Object value = method.invoke(annotation); - if (filterClasses) { + if (classValuesAsString) { if (value instanceof Class) { value = ((Class) value).getName(); } diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/AnnotationMetadata.java b/org.springframework.core/src/main/java/org/springframework/core/type/AnnotationMetadata.java index 8d2e9431791..285cc99f933 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/type/AnnotationMetadata.java +++ b/org.springframework.core/src/main/java/org/springframework/core/type/AnnotationMetadata.java @@ -83,6 +83,20 @@ public interface AnnotationMetadata extends ClassMetadata { */ Map getAnnotationAttributes(String annotationType); + /** + * Retrieve the attributes of the annotation of the given type, + * if any (i.e. if defined on the underlying class, as direct + * annotation or as meta-annotation). + * @param annotationType the annotation type to look for + * @param classValuesAsString whether to convert class references to String + * class names for exposure as values in the returned Map, instead of Class + * references which might potentially have to be loaded first + * @return a Map of attributes, with the attribute name as key (e.g. "value") + * and the defined attribute value as Map value. This return value will be + * null if no matching annotation is defined. + */ + Map getAnnotationAttributes(String annotationType, boolean classValuesAsString); + /** * Determine whether the underlying class has any methods that are * annotated (or meta-annotated) with the given annotation type. diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java b/org.springframework.core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java index 736cdf14f5b..5e413033bdf 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java +++ b/org.springframework.core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java @@ -114,14 +114,18 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements } public Map getAnnotationAttributes(String annotationType) { + return getAnnotationAttributes(annotationType, false); + } + + public Map getAnnotationAttributes(String annotationType, boolean classValuesAsString) { Annotation[] anns = getIntrospectedClass().getAnnotations(); for (Annotation ann : anns) { if (ann.annotationType().getName().equals(annotationType)) { - return AnnotationUtils.getAnnotationAttributes(ann, true); + return AnnotationUtils.getAnnotationAttributes(ann, classValuesAsString); } for (Annotation metaAnn : ann.annotationType().getAnnotations()) { if (metaAnn.annotationType().getName().equals(annotationType)) { - return AnnotationUtils.getAnnotationAttributes(metaAnn, true); + return AnnotationUtils.getAnnotationAttributes(metaAnn, classValuesAsString); } } } diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java index fc383cb617f..267dc6c5d67 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java @@ -63,11 +63,7 @@ final class AnnotationAttributesReadingVisitor implements AnnotationVisitor { public void visit(String name, Object value) { - Object valueToUse = value; - if (valueToUse instanceof Type) { - valueToUse = ((Type) value).getClassName(); - } - this.localAttributes.put(name, valueToUse); + this.localAttributes.put(name, value); } public void visitEnum(String name, String desc, String value) { @@ -93,9 +89,6 @@ final class AnnotationAttributesReadingVisitor implements AnnotationVisitor { return new AnnotationVisitor() { public void visit(String name, Object value) { Object newValue = value; - if (newValue instanceof Type) { - newValue = ((Type) value).getClassName(); - } Object existingValue = localAttributes.get(attrName); if (existingValue != null) { newValue = ObjectUtils.addObjectToArray((Object[]) existingValue, newValue); diff --git a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java index 05548489492..2b59804c321 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java @@ -97,7 +97,38 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor } public Map getAnnotationAttributes(String annotationType) { - return this.attributeMap.get(annotationType); + return getAnnotationAttributes(annotationType, false); + } + + public Map getAnnotationAttributes(String annotationType, boolean classValuesAsString) { + Map raw = this.attributeMap.get(annotationType); + if (raw == null) { + return null; + } + Map result = new LinkedHashMap(raw.size()); + for (Map.Entry entry : raw.entrySet()) { + try { + Object value = entry.getValue(); + if (value instanceof Type) { + value = (classValuesAsString ? ((Type) value).getClassName() : + this.classLoader.loadClass(((Type) value).getClassName())); + } + else if (value instanceof Type[]) { + Type[] array = (Type[]) value; + Object[] convArray = (classValuesAsString ? new String[array.length] : new Class[array.length]); + for (int i = 0; i < array.length; i++) { + convArray[i] = (classValuesAsString ? array[i].getClassName() : + this.classLoader.loadClass(array[i].getClassName())); + } + value = convArray; + } + result.put(entry.getKey(), value); + } + catch (Exception ex) { + // Class not found - can't resolve class reference in annotation attribute. + } + } + return result; } public boolean hasAnnotatedMethods(String annotationType) {