AnnotationMetadata returns Class values by default (again), allowing for explicit retrieval of String class names where preferred (SPR-5827)

This commit is contained in:
Juergen Hoeller 2009-11-11 19:11:41 +00:00
parent afdb96ab2c
commit bbd7fb3969
9 changed files with 96 additions and 45 deletions

View File

@ -24,7 +24,6 @@ import java.util.Set;
import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter; 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.DescriptiveResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
@ -51,7 +50,7 @@ final class ConfigurationClass {
private String beanName; private String beanName;
private final Map<String, String> importedResources = new LinkedHashMap<String, String>(); private final Map<String, Class> importedResources = new LinkedHashMap<String, Class>();
private final Set<ConfigurationClassMethod> methods = new LinkedHashSet<ConfigurationClassMethod>(); private final Set<ConfigurationClassMethod> methods = new LinkedHashSet<ConfigurationClassMethod>();
@ -108,11 +107,11 @@ final class ConfigurationClass {
return this.methods; return this.methods;
} }
public void addImportedResource(String importedResource, String readerClassName) { public void addImportedResource(String importedResource, Class readerClass) {
this.importedResources.put(importedResource, readerClassName); this.importedResources.put(importedResource, readerClass);
} }
public Map<String, String> getImportedResources() { public Map<String, Class> getImportedResources() {
return this.importedResources; return this.importedResources;
} }

View File

@ -26,6 +26,7 @@ import java.util.Set;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor; 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.io.Resource;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.MethodMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -214,28 +213,24 @@ class ConfigurationClassBeanDefinitionReader {
registry.registerBeanDefinition(beanName, beanDefToRegister); registry.registerBeanDefinition(beanName, beanDefToRegister);
} }
private void loadBeanDefinitionsFromImportedResources(Map<String, String> importedResources) { private void loadBeanDefinitionsFromImportedResources(Map<String, Class> importedResources) {
Map<Class, BeanDefinitionReader> readerInstanceCache = new HashMap<Class, BeanDefinitionReader>();
HashMap<String, BeanDefinitionReader> readerInstanceCache = new HashMap<String, BeanDefinitionReader>(); for (Map.Entry<String, Class> entry : importedResources.entrySet()) {
String resource = entry.getKey();
for (String resource : importedResources.keySet()) { Class readerClass = entry.getValue();
String readerClassName = importedResources.get(resource); if (!readerInstanceCache.containsKey(readerClass)) {
if (!readerInstanceCache.containsKey(readerClassName)) {
try { try {
@SuppressWarnings("unchecked") BeanDefinitionReader readerInstance = (BeanDefinitionReader)
Class<? extends BeanDefinitionReader> readerClass = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
(Class<? extends BeanDefinitionReader>) ClassUtils.forName(readerClassName, ClassUtils.getDefaultClassLoader()); readerInstanceCache.put(readerClass, readerInstance);
BeanDefinitionReader readerInstance = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry); }
readerInstanceCache.put(readerClassName, readerInstance); catch (Exception ex) {
} catch (Exception ex) { throw new IllegalStateException("Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
ReflectionUtils.handleReflectionException(ex);
} }
} }
BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
BeanDefinitionReader reader = readerInstanceCache.get(readerClassName);
// TODO SPR-6310: qualify relatively pathed locations as done in AbstractContextLoader.modifyLocations // TODO SPR-6310: qualify relatively pathed locations as done in AbstractContextLoader.modifyLocations
reader.loadBeanDefinitions(importedResources.keySet().toArray(new String[]{})); reader.loadBeanDefinitions(resource);
} }
} }

View File

@ -28,14 +28,13 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.StandardAnnotationMetadata; import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory; 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 * 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 { protected void doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
if (metadata.isAnnotated(Import.class.getName())) { 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())) { if (metadata.isAnnotated(ImportResource.class.getName())) {
String readerClassName = (String) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("reader"); String[] resources = (String[]) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("value");
for (String importedResource : (String[]) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("value")) { Class readerClass = (Class) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("reader");
configClass.addImportedResource(importedResource, readerClassName); 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<MethodMetadata> methods = metadata.getAnnotatedMethods(Bean.class.getName()); Set<MethodMetadata> methods = metadata.getAnnotatedMethods(Bean.class.getName());

View File

@ -60,6 +60,17 @@ public class ImportTests {
assertBeanDefinitionCount((configClasses + beansInClasses), ConfigurationWithImportAnnotation.class); 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 @Test
public void testProcessImportsWithDoubleImports() { public void testProcessImportsWithDoubleImports() {
int configClasses = 3; int configClasses = 3;

View File

@ -286,20 +286,20 @@ public abstract class AnnotationUtils {
/** /**
* Retrieve the given annotation's attributes as a Map. * Retrieve the given annotation's attributes as a Map.
* @param annotation the annotation to retrieve the attributes for * @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} * (for compatibility with {@link org.springframework.core.type.AnnotationMetadata}
* or to preserve them as Class references * or to preserve them as Class references
* @return the Map of annotation attributes, with attribute names as keys * @return the Map of annotation attributes, with attribute names as keys
* and corresponding attribute values as values * and corresponding attribute values as values
*/ */
public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean filterClasses) { public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) {
Map<String, Object> attrs = new HashMap<String, Object>(); Map<String, Object> attrs = new HashMap<String, Object>();
Method[] methods = annotation.annotationType().getDeclaredMethods(); Method[] methods = annotation.annotationType().getDeclaredMethods();
for (Method method : methods) { for (Method method : methods) {
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) { if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
try { try {
Object value = method.invoke(annotation); Object value = method.invoke(annotation);
if (filterClasses) { if (classValuesAsString) {
if (value instanceof Class) { if (value instanceof Class) {
value = ((Class) value).getName(); value = ((Class) value).getName();
} }

View File

@ -83,6 +83,20 @@ public interface AnnotationMetadata extends ClassMetadata {
*/ */
Map<String, Object> getAnnotationAttributes(String annotationType); Map<String, Object> 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
* <code>null</code> if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);
/** /**
* Determine whether the underlying class has any methods that are * Determine whether the underlying class has any methods that are
* annotated (or meta-annotated) with the given annotation type. * annotated (or meta-annotated) with the given annotation type.

View File

@ -114,14 +114,18 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
} }
public Map<String, Object> getAnnotationAttributes(String annotationType) { public Map<String, Object> getAnnotationAttributes(String annotationType) {
return getAnnotationAttributes(annotationType, false);
}
public Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
Annotation[] anns = getIntrospectedClass().getAnnotations(); Annotation[] anns = getIntrospectedClass().getAnnotations();
for (Annotation ann : anns) { for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) { if (ann.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(ann, true); return AnnotationUtils.getAnnotationAttributes(ann, classValuesAsString);
} }
for (Annotation metaAnn : ann.annotationType().getAnnotations()) { for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) { if (metaAnn.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(metaAnn, true); return AnnotationUtils.getAnnotationAttributes(metaAnn, classValuesAsString);
} }
} }
} }

View File

@ -63,11 +63,7 @@ final class AnnotationAttributesReadingVisitor implements AnnotationVisitor {
public void visit(String name, Object value) { public void visit(String name, Object value) {
Object valueToUse = value; this.localAttributes.put(name, value);
if (valueToUse instanceof Type) {
valueToUse = ((Type) value).getClassName();
}
this.localAttributes.put(name, valueToUse);
} }
public void visitEnum(String name, String desc, String value) { public void visitEnum(String name, String desc, String value) {
@ -93,9 +89,6 @@ final class AnnotationAttributesReadingVisitor implements AnnotationVisitor {
return new AnnotationVisitor() { return new AnnotationVisitor() {
public void visit(String name, Object value) { public void visit(String name, Object value) {
Object newValue = value; Object newValue = value;
if (newValue instanceof Type) {
newValue = ((Type) value).getClassName();
}
Object existingValue = localAttributes.get(attrName); Object existingValue = localAttributes.get(attrName);
if (existingValue != null) { if (existingValue != null) {
newValue = ObjectUtils.addObjectToArray((Object[]) existingValue, newValue); newValue = ObjectUtils.addObjectToArray((Object[]) existingValue, newValue);

View File

@ -97,7 +97,38 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor
} }
public Map<String, Object> getAnnotationAttributes(String annotationType) { public Map<String, Object> getAnnotationAttributes(String annotationType) {
return this.attributeMap.get(annotationType); return getAnnotationAttributes(annotationType, false);
}
public Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
Map<String, Object> raw = this.attributeMap.get(annotationType);
if (raw == null) {
return null;
}
Map<String, Object> result = new LinkedHashMap<String, Object>(raw.size());
for (Map.Entry<String, Object> 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) { public boolean hasAnnotatedMethods(String annotationType) {