next cut of JavaConfig metadata reading revision: using cached MetadataReaders
This commit is contained in:
@@ -192,8 +192,8 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
|
||||
}
|
||||
if (qualifier == null) {
|
||||
Annotation targetAnnotation = null;
|
||||
if (bd.getFactoryMethodForIntrospection() != null) {
|
||||
targetAnnotation = bd.getFactoryMethodForIntrospection().getAnnotation(type);
|
||||
if (bd.getResolvedFactoryMethod() != null) {
|
||||
targetAnnotation = bd.getResolvedFactoryMethod().getAnnotation(type);
|
||||
}
|
||||
if (targetAnnotation == null && bd.hasBeanClass()) {
|
||||
// look for matching annotation on the target class
|
||||
|
||||
@@ -1025,7 +1025,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
if (mbd == null) {
|
||||
if (bd.getParentName() == null) {
|
||||
// Use copy of given root bean definition.
|
||||
mbd = new RootBeanDefinition(bd);
|
||||
if (bd instanceof RootBeanDefinition) {
|
||||
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
|
||||
}
|
||||
else {
|
||||
mbd = new RootBeanDefinition(bd);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Child bean definition: needs to be merged with parent.
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
@@ -43,10 +44,10 @@ import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MethodInvoker;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Helper class for resolving constructors and factory methods.
|
||||
@@ -247,6 +248,36 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the factory method in the specified bean definition, if possible.
|
||||
* {@link RootBeanDefinition#getResolvedFactoryMethod()} can be checked for the result.
|
||||
* @param mbd the bean definition to check
|
||||
*/
|
||||
public void resolveFactoryMethodIfPossible(RootBeanDefinition mbd) {
|
||||
Class factoryClass;
|
||||
if (mbd.getFactoryBeanName() != null) {
|
||||
factoryClass = this.beanFactory.getType(mbd.getFactoryBeanName());
|
||||
}
|
||||
else {
|
||||
factoryClass = mbd.getBeanClass();
|
||||
}
|
||||
factoryClass = ClassUtils.getUserClass(factoryClass);
|
||||
Method[] candidates = ReflectionUtils.getAllDeclaredMethods(factoryClass);
|
||||
Method uniqueCandidate = null;
|
||||
for (Method candidate : candidates) {
|
||||
if (mbd.isFactoryMethod(candidate)) {
|
||||
if (uniqueCandidate == null) {
|
||||
uniqueCandidate = candidate;
|
||||
}
|
||||
else if (!Arrays.equals(uniqueCandidate.getParameterTypes(), candidate.getParameterTypes())) {
|
||||
uniqueCandidate = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate the bean using a named factory method. The method may be static, if the
|
||||
* bean definition parameter specifies a class, rather than a "factory-bean", or
|
||||
@@ -306,13 +337,13 @@ class ConstructorResolver {
|
||||
if (factoryMethodToUse != null) {
|
||||
// Found a cached factory method...
|
||||
argsToUse = mbd.resolvedConstructorArguments;
|
||||
if (argsToUse == null) {
|
||||
if (argsToUse == null && mbd.preparedConstructorArguments != null) {
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (factoryMethodToUse == null) {
|
||||
if (factoryMethodToUse == null || argsToUse == null) {
|
||||
// Need to determine the factory method...
|
||||
// Try all methods with this name to see if they match the given arguments.
|
||||
factoryClass = ClassUtils.getUserClass(factoryClass);
|
||||
@@ -320,7 +351,8 @@ class ConstructorResolver {
|
||||
List<Method> candidateSet = new ArrayList<Method>();
|
||||
for (Method candidate : rawCandidates) {
|
||||
if (Modifier.isStatic(candidate.getModifiers()) == isStatic &&
|
||||
candidate.getName().equals(mbd.getFactoryMethodName())) {
|
||||
candidate.getName().equals(mbd.getFactoryMethodName()) &&
|
||||
mbd.isFactoryMethod(candidate)) {
|
||||
candidateSet.add(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -49,9 +47,7 @@ import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -432,31 +428,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
*/
|
||||
protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor) {
|
||||
resolveBeanClass(mbd, beanName);
|
||||
// TODO: the following is duplicating the factory method resolution algorithm in
|
||||
// ConstructorResolver quite a bit...
|
||||
if (mbd.getFactoryMethodName() != null && mbd.factoryMethodForIntrospection == null) {
|
||||
Class factoryClass;
|
||||
if (mbd.getFactoryBeanName() != null) {
|
||||
factoryClass = getType(mbd.getFactoryBeanName());
|
||||
}
|
||||
else {
|
||||
factoryClass = mbd.getBeanClass();
|
||||
}
|
||||
factoryClass = ClassUtils.getUserClass(factoryClass);
|
||||
Method[] candidates = ReflectionUtils.getAllDeclaredMethods(factoryClass);
|
||||
Method uniqueCandidate = null;
|
||||
for (Method candidate : candidates) {
|
||||
if (candidate.getName().equals(mbd.getFactoryMethodName())) {
|
||||
if (uniqueCandidate == null) {
|
||||
uniqueCandidate = candidate;
|
||||
}
|
||||
else if (!Arrays.equals(uniqueCandidate.getParameterTypes(), candidate.getParameterTypes())) {
|
||||
uniqueCandidate = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mbd.factoryMethodForIntrospection = uniqueCandidate;
|
||||
if (mbd.isFactoryMethodUnique && mbd.resolvedConstructorOrFactoryMethod == null) {
|
||||
new ConstructorResolver(this, null, null, null).resolveFactoryMethodIfPossible(mbd);
|
||||
}
|
||||
return getAutowireCandidateResolver().isAutowireCandidate(
|
||||
new BeanDefinitionHolder(mbd, beanName, getAliases(beanName)), descriptor);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,6 +25,7 @@ import java.util.Set;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A root bean definition represents the merged bean definition that backs
|
||||
@@ -52,7 +53,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
|
||||
private final Set<String> externallyManagedDestroyMethods = Collections.synchronizedSet(new HashSet<String>());
|
||||
|
||||
volatile Method factoryMethodForIntrospection;
|
||||
boolean isFactoryMethodUnique;
|
||||
|
||||
/** Package-visible field for caching the resolved constructor or factory method */
|
||||
volatile Object resolvedConstructorOrFactoryMethod;
|
||||
@@ -198,7 +199,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
* @param original the original bean definition to copy from
|
||||
*/
|
||||
public RootBeanDefinition(RootBeanDefinition original) {
|
||||
super((BeanDefinition) original);
|
||||
this((BeanDefinition) original);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,6 +209,9 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
*/
|
||||
RootBeanDefinition(BeanDefinition original) {
|
||||
super(original);
|
||||
if (original instanceof RootBeanDefinition) {
|
||||
this.isFactoryMethodUnique = ((RootBeanDefinition) original).isFactoryMethodUnique;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,8 +225,26 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
public Method getFactoryMethodForIntrospection() {
|
||||
return this.factoryMethodForIntrospection;
|
||||
/**
|
||||
* Specify a factory method name that refers to a non-overloaded method.
|
||||
*/
|
||||
public void setUniqueFactoryMethodName(String name) {
|
||||
Assert.hasText(name, "Factory method name must not be empty");
|
||||
setFactoryMethodName(name);
|
||||
this.isFactoryMethodUnique = true;
|
||||
}
|
||||
|
||||
public boolean isFactoryMethod(Method candidate) {
|
||||
return (candidate != null && candidate.getName().equals(getFactoryMethodName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resolved factory method as a Java Method object, if available.
|
||||
* @return the factory method, or <code>null</code> if not found or not resolved yet
|
||||
*/
|
||||
public Method getResolvedFactoryMethod() {
|
||||
Object candidate = this.resolvedConstructorOrFactoryMethod;
|
||||
return (candidate instanceof Method ? (Method) candidate : null);
|
||||
}
|
||||
|
||||
|
||||
@@ -252,7 +274,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
|
||||
|
||||
@Override
|
||||
public AbstractBeanDefinition cloneBeanDefinition() {
|
||||
public RootBeanDefinition cloneBeanDefinition() {
|
||||
return new RootBeanDefinition(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ package org.springframework.context.annotation;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
@@ -46,26 +49,21 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||
|
||||
private String name;
|
||||
|
||||
private transient Object source;
|
||||
private ConfigurationClass declaringClass;
|
||||
|
||||
private Object source;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private int modifiers;
|
||||
|
||||
private Set<Annotation> annotations = new HashSet<Annotation>();
|
||||
private final Set<Annotation> annotations = new HashSet<Annotation>();
|
||||
|
||||
private Set<ConfigurationClassMethod> methods = new HashSet<ConfigurationClassMethod>();
|
||||
private final Set<ConfigurationClassMethod> methods = new LinkedHashSet<ConfigurationClassMethod>();
|
||||
|
||||
private ConfigurationClass declaringClass;
|
||||
private final Map<String, Integer> overloadedMethodMap = new LinkedHashMap<String, Integer>();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the fully-qualified name of this class.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fully-qualified name of this class.
|
||||
*/
|
||||
@@ -73,6 +71,13 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||
this.name = className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fully-qualified name of this class.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the non-qualified name of this class. Given com.acme.Foo, returns 'Foo'.
|
||||
*/
|
||||
@@ -80,12 +85,12 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||
return name == null ? null : ClassUtils.getShortName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a resource path-formatted representation of the .java file that declares this
|
||||
* class
|
||||
*/
|
||||
public Object getSource() {
|
||||
return source;
|
||||
public void setDeclaringClass(ConfigurationClass configurationClass) {
|
||||
this.declaringClass = configurationClass;
|
||||
}
|
||||
|
||||
public ConfigurationClass getDeclaringClass() {
|
||||
return declaringClass;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,6 +101,14 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a resource path-formatted representation of the .java file that declares this
|
||||
* class
|
||||
*/
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
if (getName() == null) {
|
||||
throw new IllegalStateException("'name' property is null. Call setName() before calling getLocation()");
|
||||
@@ -103,23 +116,23 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||
return new Location(new ClassPathResource(ClassUtils.convertClassNameToResourcePath(getName())), getSource());
|
||||
}
|
||||
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
|
||||
public void setModifiers(int modifiers) {
|
||||
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative");
|
||||
this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
public void addAnnotation(Annotation annotation) {
|
||||
this.annotations.add(annotation);
|
||||
}
|
||||
@@ -153,26 +166,34 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||
return anno;
|
||||
}
|
||||
|
||||
public ConfigurationClass addMethod(ConfigurationClassMethod method) {
|
||||
method.setDeclaringClass(this);
|
||||
methods.add(method);
|
||||
Integer count = overloadedMethodMap.get(method.getName());
|
||||
if (count != null) {
|
||||
overloadedMethodMap.put(method.getName(), count + 1);
|
||||
}
|
||||
else {
|
||||
overloadedMethodMap.put(method.getName(), 1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Set<ConfigurationClassMethod> getBeanMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public ConfigurationClass addMethod(ConfigurationClassMethod method) {
|
||||
method.setDeclaringClass(this);
|
||||
methods.add(method);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigurationClass getDeclaringClass() {
|
||||
return declaringClass;
|
||||
}
|
||||
|
||||
public void setDeclaringClass(ConfigurationClass configurationClass) {
|
||||
this.declaringClass = configurationClass;
|
||||
}
|
||||
|
||||
public void validate(ProblemReporter problemReporter) {
|
||||
// a configuration class may not be final (CGLIB limitation)
|
||||
// No overloading of factory methods allowed
|
||||
for (Map.Entry<String, Integer> entry : overloadedMethodMap.entrySet()) {
|
||||
String methodName = entry.getKey();
|
||||
int count = entry.getValue();
|
||||
if (count > 1) {
|
||||
problemReporter.error(new OverloadedMethodProblem(methodName, count));
|
||||
}
|
||||
}
|
||||
|
||||
// A configuration class may not be final (CGLIB limitation)
|
||||
if (getAnnotation(Configuration.class) != null) {
|
||||
if (Modifier.isFinal(modifiers)) {
|
||||
problemReporter.error(new FinalConfigurationProblem());
|
||||
@@ -188,10 +209,21 @@ final class ConfigurationClass implements BeanMetadataElement {
|
||||
private class FinalConfigurationProblem extends Problem {
|
||||
|
||||
public FinalConfigurationProblem() {
|
||||
super(String.format("@Configuration class [%s] may not be final. Remove the final modifier to continue.",
|
||||
super(String.format("@Configuration class '%s' may not be final. Remove the final modifier to continue.",
|
||||
getSimpleName()), ConfigurationClass.this.getLocation());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Factory methods on configuration classes must not be overloaded. */
|
||||
private class OverloadedMethodProblem extends Problem {
|
||||
|
||||
public OverloadedMethodProblem(String methodName, int count) {
|
||||
super(String.format("@Configuration class '%s' has %s overloaded factory methods of name '%s'. " +
|
||||
"Only one factory method of the same name allowed.",
|
||||
getSimpleName(), count, methodName), ConfigurationClass.this.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.List;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
import org.springframework.asm.Type;
|
||||
@@ -57,40 +57,47 @@ class ConfigurationClassAnnotationVisitor implements AnnotationVisitor {
|
||||
|
||||
public void visit(String attribName, Object attribValue) {
|
||||
Class<?> attribReturnType = mutableAnno.getAttributeType(attribName);
|
||||
|
||||
if (attribReturnType.equals(Class.class)) {
|
||||
// the attribute type is Class -> load it and set it.
|
||||
String fqClassName = ((Type) attribValue).getClassName();
|
||||
Class<?> classVal = ConfigurationClassReaderUtils.loadToolingSafeClass(fqClassName, classLoader);
|
||||
if (classVal == null) {
|
||||
return;
|
||||
String className = ((Type) attribValue).getClassName();
|
||||
try {
|
||||
Class<?> classVal = classLoader.loadClass(className);
|
||||
if (classVal != null) {
|
||||
mutableAnno.setAttributeValue(attribName, classVal);
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException("Cannot resolve attribute type [" + className + "]", ex);
|
||||
}
|
||||
mutableAnno.setAttributeValue(attribName, classVal);
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise, assume the value can be set literally
|
||||
mutableAnno.setAttributeValue(attribName, attribValue);
|
||||
else {
|
||||
// otherwise, assume the value can be set literally
|
||||
mutableAnno.setAttributeValue(attribName, attribValue);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) {
|
||||
String enumClassName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(enumTypeDescriptor);
|
||||
Class<? extends Enum> enumClass = ConfigurationClassReaderUtils.loadToolingSafeClass(enumClassName, classLoader);
|
||||
if (enumClass == null) {
|
||||
return;
|
||||
String enumTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(enumTypeDescriptor);
|
||||
try {
|
||||
Class<? extends Enum> enumType = (Class<? extends Enum>) classLoader.loadClass(enumTypeName);
|
||||
if (enumType == null) {
|
||||
return;
|
||||
}
|
||||
Enum enumValue = Enum.valueOf(enumType, strEnumValue);
|
||||
mutableAnno.setAttributeValue(attribName, enumValue);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException("Cannot resolve enum type [" + enumTypeName + "]", ex);
|
||||
}
|
||||
Enum enumValue = Enum.valueOf(enumClass, strEnumValue);
|
||||
mutableAnno.setAttributeValue(attribName, enumValue);
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) {
|
||||
String annoTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(attribAnnoTypeDesc);
|
||||
Class<? extends Annotation> annoType = ConfigurationClassReaderUtils.loadToolingSafeClass(annoTypeName, classLoader);
|
||||
if (annoType == null) {
|
||||
public AnnotationVisitor visitAnnotation(String attribName, String annoTypeDesc) {
|
||||
Class<? extends Annotation> annoClass = ConfigurationClassReaderUtils.loadAnnotationType(annoTypeDesc, classLoader);
|
||||
if (annoClass == null) {
|
||||
return new EmptyVisitor();
|
||||
}
|
||||
ConfigurationClassAnnotation anno = ConfigurationClassReaderUtils.createMutableAnnotation(annoType, classLoader);
|
||||
ConfigurationClassAnnotation anno = ConfigurationClassReaderUtils.createMutableAnnotation(annoClass, classLoader);
|
||||
try {
|
||||
Field attribute = mutableAnno.getClass().getField(attribName);
|
||||
attribute.set(mutableAnno, anno);
|
||||
@@ -123,14 +130,12 @@ class ConfigurationClassAnnotationVisitor implements AnnotationVisitor {
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
|
||||
public MutableAnnotationArrayVisitor(ConfigurationClassAnnotation mutableAnno, String attribName, ClassLoader classLoader) {
|
||||
this.mutableAnno = mutableAnno;
|
||||
this.attribName = attribName;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
||||
public void visit(String na, Object value) {
|
||||
values.add(value);
|
||||
}
|
||||
@@ -139,12 +144,11 @@ class ConfigurationClassAnnotationVisitor implements AnnotationVisitor {
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) {
|
||||
String annoTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(annoTypeDesc);
|
||||
Class<? extends Annotation> annoType = ConfigurationClassReaderUtils.loadToolingSafeClass(annoTypeName, classLoader);
|
||||
if (annoType == null) {
|
||||
Class<? extends Annotation> annoClass = ConfigurationClassReaderUtils.loadAnnotationType(annoTypeDesc, classLoader);
|
||||
if (annoClass == null) {
|
||||
return new EmptyVisitor();
|
||||
}
|
||||
ConfigurationClassAnnotation anno = ConfigurationClassReaderUtils.createMutableAnnotation(annoType, classLoader);
|
||||
ConfigurationClassAnnotation anno = ConfigurationClassReaderUtils.createMutableAnnotation(annoClass, classLoader);
|
||||
values.add(anno);
|
||||
return new ConfigurationClassAnnotationVisitor(anno, classLoader);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -25,6 +26,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
@@ -32,7 +34,6 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -105,7 +106,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
|
||||
ConfigurationClass configClass = method.getDeclaringClass();
|
||||
beanDef.setFactoryBeanName(configClass.getBeanName());
|
||||
beanDef.setFactoryMethodName(method.getName());
|
||||
beanDef.setUniqueFactoryMethodName(method.getName());
|
||||
beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
|
||||
// consider name and any aliases
|
||||
@@ -197,11 +198,27 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
/**
|
||||
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition created
|
||||
* by JavaConfig as opposed to any other configuration source. Used in bean overriding cases
|
||||
* where it's necessary to determine whether the bean definition was created externally
|
||||
* (e.g. via XML).
|
||||
* where it's necessary to determine whether the bean definition was created externally.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private class ConfigurationClassBeanDefinition extends RootBeanDefinition {
|
||||
|
||||
public ConfigurationClassBeanDefinition() {
|
||||
}
|
||||
|
||||
private ConfigurationClassBeanDefinition(ConfigurationClassBeanDefinition original) {
|
||||
super(original);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFactoryMethod(Method candidate) {
|
||||
return (super.isFactoryMethod(candidate) && candidate.isAnnotationPresent(Bean.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationClassBeanDefinition cloneBeanDefinition() {
|
||||
return new ConfigurationClassBeanDefinition(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReader;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
|
||||
/**
|
||||
* Parses a {@link Configuration} class definition, populating a configuration model.
|
||||
@@ -41,11 +41,11 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
class ConfigurationClassParser {
|
||||
|
||||
private final Set<ConfigurationClass> model;
|
||||
private final SimpleMetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
private final ProblemReporter problemReporter;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
private final Set<ConfigurationClass> model;
|
||||
|
||||
|
||||
/**
|
||||
@@ -53,10 +53,10 @@ class ConfigurationClassParser {
|
||||
* configuration model.
|
||||
* @param model model to be populated by each successive call to {@link #parse}
|
||||
*/
|
||||
public ConfigurationClassParser(ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
this.model = new LinkedHashSet<ConfigurationClass>();
|
||||
public ConfigurationClassParser(SimpleMetadataReaderFactory metadataReaderFactory, ProblemReporter problemReporter) {
|
||||
this.metadataReaderFactory = metadataReaderFactory;
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
this.model = new LinkedHashSet<ConfigurationClass>();
|
||||
}
|
||||
|
||||
|
||||
@@ -66,12 +66,11 @@ class ConfigurationClassParser {
|
||||
* @param beanName may be null, but if populated represents the bean id
|
||||
* (assumes that this configuration class was configured via XML)
|
||||
*/
|
||||
public void parse(String className, String beanName) {
|
||||
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
|
||||
ClassReader configClassReader = ConfigurationClassReaderUtils.newAsmClassReader(ConfigurationClassReaderUtils.getClassAsStream(resourcePath, classLoader));
|
||||
public void parse(String className, String beanName) throws IOException {
|
||||
SimpleMetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
|
||||
ConfigurationClass configClass = new ConfigurationClass();
|
||||
configClass.setBeanName(beanName);
|
||||
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
|
||||
reader.getClassReader().accept(new ConfigurationClassVisitor(configClass, model, problemReporter, metadataReaderFactory), false);
|
||||
model.add(configClass);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.core.Conventions;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -81,6 +82,8 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||
|
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
private SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
|
||||
|
||||
|
||||
/**
|
||||
* Override the default {@link ProblemReporter}.
|
||||
@@ -92,6 +95,7 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
this.metadataReaderFactory = new CachingMetadataReaderFactory(beanClassLoader);
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
@@ -132,9 +136,15 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||
}
|
||||
|
||||
// Populate a new configuration model by parsing each @Configuration classes
|
||||
ConfigurationClassParser parser = new ConfigurationClassParser(this.problemReporter, this.beanClassLoader);
|
||||
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter);
|
||||
for (BeanDefinitionHolder holder : configBeanDefs) {
|
||||
parser.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
|
||||
String beanClassName = holder.getBeanDefinition().getBeanClassName();
|
||||
try {
|
||||
parser.parse(beanClassName, holder.getBeanName());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new BeanDefinitionStoreException("Failed to load bean class [" + beanClassName + "]", ex);
|
||||
}
|
||||
}
|
||||
parser.validate();
|
||||
|
||||
@@ -208,11 +218,10 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(this.beanClassLoader);
|
||||
String className = beanDef.getBeanClassName();
|
||||
while (className != null && !(className.equals(Object.class.getName()))) {
|
||||
try {
|
||||
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
|
||||
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(className);
|
||||
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
|
||||
if (metadata.hasAnnotation(Configuration.class.getName())) {
|
||||
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "full");
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import static java.lang.String.*;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
@@ -26,17 +24,11 @@ import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -48,8 +40,6 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
class ConfigurationClassReaderUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ConfigurationClassReaderUtils.class);
|
||||
|
||||
/**
|
||||
* Convert a type descriptor to a classname suitable for classloading with
|
||||
* Class.forName().
|
||||
@@ -92,94 +82,14 @@ class ConfigurationClassReaderUtils {
|
||||
return convertAsmTypeDescriptorToClassName(returnTypeDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ASM {@link ClassReader} for <var>pathToClass</var>. Appends '.class' to
|
||||
* pathToClass before attempting to load.
|
||||
*/
|
||||
public static ClassReader newAsmClassReader(String pathToClass, ClassLoader classLoader) {
|
||||
InputStream is = getClassAsStream(pathToClass, classLoader);
|
||||
return newAsmClassReader(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that creates and returns a new ASM {@link ClassReader} for the
|
||||
* given InputStream <var>is</var>, closing the InputStream after creating the
|
||||
* ClassReader and rethrowing any IOException thrown during ClassReader instantiation as
|
||||
* an unchecked exception. Logs and ignores any IOException thrown when closing the
|
||||
* InputStream.
|
||||
*
|
||||
* @param is InputStream that will be provided to the new ClassReader instance.
|
||||
*/
|
||||
public static ClassReader newAsmClassReader(InputStream is) {
|
||||
try {
|
||||
return new ClassReader(is);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new BeanDefinitionStoreException("An unexpected exception occurred while creating ASM ClassReader: " + ex);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
is.close();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the default ClassLoader to load <var>pathToClass</var>. Appends '.class' to
|
||||
* pathToClass before attempting to load.
|
||||
* @param pathToClass resource path to class, not including .class suffix. e.g.: com/acme/MyClass
|
||||
* @return inputStream for <var>pathToClass</var>
|
||||
* @throws RuntimeException if <var>pathToClass</var> does not exist
|
||||
*/
|
||||
public static InputStream getClassAsStream(String pathToClass, ClassLoader classLoader) {
|
||||
String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX;
|
||||
InputStream is = classLoader.getResourceAsStream(classFileName);
|
||||
if (is == null) {
|
||||
throw new IllegalStateException("Class file [" + classFileName + "] not found");
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the specified class using the default class loader, gracefully handling any
|
||||
* {@link ClassNotFoundException} that may be thrown by issuing a WARN level logging
|
||||
* statement and return null. This functionality is specifically implemented to
|
||||
* accomodate tooling (Spring IDE) concerns, where user-defined types will not be
|
||||
* available to the tooling.
|
||||
* <p>
|
||||
* Because {@link ClassNotFoundException} is compensated for by returning null, callers
|
||||
* must take care to handle the null case appropriately.
|
||||
* <p>
|
||||
* In cases where the WARN logging statement is not desired, use the
|
||||
* {@link #loadClass(String)} method, which returns null but issues no logging
|
||||
* statements.
|
||||
* <p>
|
||||
* This method should only ever return null in the case of a user-defined type be
|
||||
* processed at tooling time. Therefore, tooling may not be able to represent any custom
|
||||
* annotation semantics, but JavaConfig itself will not have any problem loading and
|
||||
* respecting them at actual runtime.
|
||||
*
|
||||
* @param <T> type of class to be returned
|
||||
* @param fqClassName fully-qualified class name
|
||||
*
|
||||
* @return newly loaded class, null if class could not be found.
|
||||
*
|
||||
* @see #loadClass(String)
|
||||
* @see #loadRequiredClass(String)
|
||||
* @see ClassUtils#getDefaultClassLoader()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<? extends T> loadToolingSafeClass(String fqClassName, ClassLoader classLoader) {
|
||||
public static Class<? extends Annotation> loadAnnotationType(String annoTypeDesc, ClassLoader classLoader) {
|
||||
String annoTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(annoTypeDesc);
|
||||
try {
|
||||
return (Class<? extends T>) classLoader.loadClass(fqClassName);
|
||||
return (Class<? extends Annotation>) classLoader.loadClass(annoTypeName);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
logger.warn(String.format("Unable to load class [%s], likely due to tooling-specific restrictions."
|
||||
+ "Attempting to continue, but unexpected errors may occur", fqClassName), ex);
|
||||
return null;
|
||||
throw new IllegalStateException("Could not load annotation type [" + annoTypeName + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +122,7 @@ class ConfigurationClassReaderUtils {
|
||||
// and default values of the attributes defined in 'annoType'
|
||||
Method[] attribs = annoType.getDeclaredMethods();
|
||||
for (Method attrib : attribs) {
|
||||
this.attributes.put(attrib.getName(), getDefaultValue(annoType, attrib.getName()));
|
||||
this.attributes.put(attrib.getName(), AnnotationUtils.getDefaultValue(annoType, attrib.getName()));
|
||||
this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName()));
|
||||
}
|
||||
|
||||
@@ -349,11 +259,10 @@ class ConfigurationClassReaderUtils {
|
||||
}
|
||||
|
||||
private String getAttribs() {
|
||||
ArrayList<String> attribs = new ArrayList<String>();
|
||||
|
||||
for (String attribName : attributes.keySet())
|
||||
List<String> attribs = new ArrayList<String>();
|
||||
for (String attribName : attributes.keySet()) {
|
||||
attribs.add(format("%s=%s", attribName, attributes.get(attribName)));
|
||||
|
||||
}
|
||||
return StringUtils.collectionToDelimitedString(attribs, ", ");
|
||||
}
|
||||
|
||||
@@ -361,15 +270,12 @@ class ConfigurationClassReaderUtils {
|
||||
* Retrieve the type of the given annotation attribute.
|
||||
*/
|
||||
private static Class<?> getAttributeType(Class<? extends Annotation> annotationType, String attributeName) {
|
||||
Method method = null;
|
||||
|
||||
try {
|
||||
method = annotationType.getDeclaredMethod(attributeName);
|
||||
} catch (Exception ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
return annotationType.getDeclaredMethod(attributeName).getReturnType();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Could not introspect return type", ex);
|
||||
}
|
||||
|
||||
return method.getReturnType();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -37,10 +38,13 @@ import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.asm.commons.EmptyVisitor;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.parsing.Location;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReader;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -64,27 +68,28 @@ class ConfigurationClassVisitor implements ClassVisitor {
|
||||
|
||||
private final ProblemReporter problemReporter;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
private final SimpleMetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
private final Stack<ConfigurationClass> importStack;
|
||||
|
||||
|
||||
public ConfigurationClassVisitor(ConfigurationClass configClass, Set<ConfigurationClass> model,
|
||||
ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
ProblemReporter problemReporter, SimpleMetadataReaderFactory metadataReaderFactory) {
|
||||
|
||||
this.configClass = configClass;
|
||||
this.model = model;
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
this.metadataReaderFactory = metadataReaderFactory;
|
||||
this.importStack = new ImportStack();
|
||||
}
|
||||
|
||||
private ConfigurationClassVisitor(ConfigurationClass configClass, Set<ConfigurationClass> model,
|
||||
ProblemReporter problemReporter, ClassLoader classLoader, Stack<ConfigurationClass> importStack) {
|
||||
ProblemReporter problemReporter, SimpleMetadataReaderFactory metadataReaderFactory,
|
||||
Stack<ConfigurationClass> importStack) {
|
||||
this.configClass = configClass;
|
||||
this.model = model;
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
this.metadataReaderFactory = metadataReaderFactory;
|
||||
this.importStack = importStack;
|
||||
}
|
||||
|
||||
@@ -105,9 +110,15 @@ class ConfigurationClassVisitor implements ClassVisitor {
|
||||
if (OBJECT_DESC.equals(superTypeDesc)) {
|
||||
return;
|
||||
}
|
||||
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader, importStack);
|
||||
ClassReader reader = ConfigurationClassReaderUtils.newAsmClassReader(superTypeDesc, classLoader);
|
||||
reader.accept(visitor, false);
|
||||
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, problemReporter, metadataReaderFactory, importStack);
|
||||
String superClassName = ClassUtils.convertResourcePathToClassName(superTypeDesc);
|
||||
try {
|
||||
SimpleMetadataReader reader = this.metadataReaderFactory.getMetadataReader(superClassName);
|
||||
reader.getClassReader().accept(visitor, false);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new BeanDefinitionStoreException("Failed to load bean super class [" + superClassName + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitSource(String sourceFile, String debug) {
|
||||
@@ -132,15 +143,14 @@ class ConfigurationClassVisitor implements ClassVisitor {
|
||||
* @see Lazy
|
||||
* @see Import
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
||||
String annoTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(annoTypeDesc);
|
||||
Class<? extends Annotation> annoClass = ConfigurationClassReaderUtils.loadToolingSafeClass(annoTypeName, classLoader);
|
||||
|
||||
ClassLoader classLoader = metadataReaderFactory.getResourceLoader().getClassLoader();
|
||||
Class<? extends Annotation> annoClass = ConfigurationClassReaderUtils.loadAnnotationType(annoTypeDesc, classLoader);
|
||||
if (annoClass == null) {
|
||||
// annotation was unable to be loaded -> probably Spring IDE unable to load a user-defined annotation
|
||||
return new EmptyVisitor();
|
||||
}
|
||||
|
||||
if (Import.class.equals(annoClass)) {
|
||||
if (!importStack.contains(configClass)) {
|
||||
importStack.push(configClass);
|
||||
@@ -169,6 +179,7 @@ class ConfigurationClassVisitor implements ClassVisitor {
|
||||
* {@link ConfigurationClassMethodVisitor}.
|
||||
*/
|
||||
public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor, String arg3, String[] arg4) {
|
||||
ClassLoader classLoader = metadataReaderFactory.getResourceLoader().getClassLoader();
|
||||
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers, classLoader);
|
||||
}
|
||||
|
||||
@@ -181,7 +192,7 @@ class ConfigurationClassVisitor implements ClassVisitor {
|
||||
* {@link Configuration} class. Determines whether the method is a {@link Bean}
|
||||
* method and if so, adds it to the {@link ConfigurationClass}.
|
||||
*/
|
||||
private static class ConfigurationClassMethodVisitor extends MethodAdapter {
|
||||
private class ConfigurationClassMethodVisitor extends MethodAdapter {
|
||||
|
||||
private final ConfigurationClass configClass;
|
||||
private final String methodName;
|
||||
@@ -214,9 +225,9 @@ class ConfigurationClassVisitor implements ClassVisitor {
|
||||
* present (regardless of its RetentionPolicy).
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
||||
String annoClassName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(annoTypeDesc);
|
||||
Class<? extends Annotation> annoClass = ConfigurationClassReaderUtils.loadToolingSafeClass(annoClassName, classLoader);
|
||||
Class<? extends Annotation> annoClass = ConfigurationClassReaderUtils.loadAnnotationType(annoTypeDesc, classLoader);
|
||||
if (annoClass == null) {
|
||||
return super.visitAnnotation(annoTypeDesc, visible);
|
||||
}
|
||||
@@ -262,14 +273,19 @@ class ConfigurationClassVisitor implements ClassVisitor {
|
||||
private ConfigurationClassMethod.ReturnType initReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
||||
final ConfigurationClassMethod.ReturnType returnType = new ConfigurationClassMethod.ReturnType(ConfigurationClassReaderUtils.getReturnTypeFromAsmMethodDescriptor(methodDescriptor));
|
||||
// detect whether the return type is an interface
|
||||
ConfigurationClassReaderUtils.newAsmClassReader(ClassUtils.convertClassNameToResourcePath(returnType.getName()), classLoader).accept(
|
||||
new ClassAdapter(new EmptyVisitor()) {
|
||||
@Override
|
||||
public void visit(int arg0, int arg1, String arg2, String arg3, String arg4, String[] arg5) {
|
||||
returnType.setInterface((arg1 & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE);
|
||||
}
|
||||
}, false);
|
||||
return returnType;
|
||||
try {
|
||||
metadataReaderFactory.getMetadataReader(returnType.getName()).getClassReader().accept(
|
||||
new ClassAdapter(new EmptyVisitor()) {
|
||||
@Override
|
||||
public void visit(int arg0, int arg1, String arg2, String arg3, String arg4, String[] arg5) {
|
||||
returnType.setInterface((arg1 & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE);
|
||||
}
|
||||
}, false);
|
||||
return returnType;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new BeanDefinitionStoreException("Failed to load bean return type [" + returnType.getName() + "]", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,17 +301,13 @@ class ConfigurationClassVisitor implements ClassVisitor {
|
||||
|
||||
private final ProblemReporter problemReporter;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final List<String> classesToImport = new ArrayList<String>();
|
||||
|
||||
|
||||
public ImportAnnotationVisitor(
|
||||
Set<ConfigurationClass> model, ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
|
||||
this.model = model;
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public void visit(String s, Object o) {
|
||||
@@ -337,13 +349,18 @@ class ConfigurationClassVisitor implements ClassVisitor {
|
||||
|
||||
private void processClassToImport(String classToImport) {
|
||||
ConfigurationClass configClass = new ConfigurationClass();
|
||||
ClassReader reader = ConfigurationClassReaderUtils.newAsmClassReader(ClassUtils.convertClassNameToResourcePath(classToImport), classLoader);
|
||||
reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader, importStack), false);
|
||||
if (configClass.getAnnotation(Configuration.class) == null) {
|
||||
problemReporter.error(new NonAnnotatedConfigurationProblem(configClass.getName(), configClass.getLocation()));
|
||||
try {
|
||||
ClassReader reader = metadataReaderFactory.getMetadataReader(classToImport).getClassReader();
|
||||
reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, metadataReaderFactory, importStack), false);
|
||||
if (configClass.getAnnotation(Configuration.class) == null) {
|
||||
problemReporter.error(new NonAnnotatedConfigurationProblem(configClass.getName(), configClass.getLocation()));
|
||||
}
|
||||
else {
|
||||
model.add(configClass);
|
||||
}
|
||||
}
|
||||
else {
|
||||
model.add(configClass);
|
||||
catch (IOException ex) {
|
||||
throw new BeanDefinitionStoreException("Failed to load imported configuration class [" + classToImport + "]", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.context.annotation.ConfigurationClassParser;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
|
||||
/**
|
||||
* Unit test proving that ASM-based {@link ConfigurationClassParser} correctly detects circular use of
|
||||
@@ -32,9 +29,10 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class AsmCircularImportDetectionTests extends AbstractCircularImportDetectionTests {
|
||||
|
||||
@Override
|
||||
protected ConfigurationClassParser newParser() {
|
||||
return new ConfigurationClassParser(new FailFastProblemReporter(), ClassUtils.getDefaultClassLoader());
|
||||
return new ConfigurationClassParser(new CachingMetadataReaderFactory(), new FailFastProblemReporter());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.BeanAge;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -47,6 +46,11 @@ public final class FactoryMethodComponent {
|
||||
return new TestBean("publicInstance");
|
||||
}
|
||||
|
||||
// to be ignored
|
||||
public TestBean publicInstance(boolean doIt) {
|
||||
return new TestBean("publicInstance");
|
||||
}
|
||||
|
||||
@Bean @BeanAge(1)
|
||||
protected TestBean protectedInstance(@Qualifier("public") TestBean spouse, @Value("#{privateInstance.age}") String country) {
|
||||
TestBean tb = new TestBean("protectedInstance", 1);
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.springframework.core.io.ResourceLoader;
|
||||
*/
|
||||
public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory {
|
||||
|
||||
private final Map<Resource, MetadataReader> classReaderCache = new HashMap<Resource, MetadataReader>();
|
||||
private final Map<Resource, SimpleMetadataReader> classReaderCache = new HashMap<Resource, SimpleMetadataReader>();
|
||||
|
||||
|
||||
/**
|
||||
@@ -62,9 +62,9 @@ public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory {
|
||||
|
||||
|
||||
@Override
|
||||
public MetadataReader getMetadataReader(Resource resource) throws IOException {
|
||||
public SimpleMetadataReader getMetadataReader(Resource resource) throws IOException {
|
||||
synchronized (this.classReaderCache) {
|
||||
MetadataReader metadataReader = this.classReaderCache.get(resource);
|
||||
SimpleMetadataReader metadataReader = this.classReaderCache.get(resource);
|
||||
if (metadataReader == null) {
|
||||
metadataReader = super.getMetadataReader(resource);
|
||||
this.classReaderCache.put(resource, metadataReader);
|
||||
|
||||
@@ -30,29 +30,55 @@ import org.springframework.core.type.ClassMetadata;
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
class SimpleMetadataReader implements MetadataReader {
|
||||
public class SimpleMetadataReader implements MetadataReader {
|
||||
|
||||
private final ClassReader classReader;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private ClassMetadata classMetadata;
|
||||
|
||||
public SimpleMetadataReader(ClassReader classReader, ClassLoader classLoader) {
|
||||
private AnnotationMetadata annotationMetadata;
|
||||
|
||||
|
||||
SimpleMetadataReader(ClassReader classReader, ClassLoader classLoader) {
|
||||
this.classReader = classReader;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the underlying ASM ClassReader.
|
||||
*/
|
||||
public final ClassReader getClassReader() {
|
||||
return this.classReader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying ClassLoader.
|
||||
*/
|
||||
public final ClassLoader getClassLoader() {
|
||||
return this.classLoader;
|
||||
}
|
||||
|
||||
|
||||
public ClassMetadata getClassMetadata() {
|
||||
ClassMetadataReadingVisitor visitor = new ClassMetadataReadingVisitor();
|
||||
this.classReader.accept(visitor, true);
|
||||
return visitor;
|
||||
if (this.classMetadata == null) {
|
||||
ClassMetadataReadingVisitor visitor = new ClassMetadataReadingVisitor();
|
||||
this.classReader.accept(visitor, true);
|
||||
this.classMetadata = visitor;
|
||||
}
|
||||
return this.classMetadata;
|
||||
}
|
||||
|
||||
public AnnotationMetadata getAnnotationMetadata() {
|
||||
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(this.classLoader);
|
||||
this.classReader.accept(visitor, true);
|
||||
return visitor;
|
||||
if (this.annotationMetadata == null) {
|
||||
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(this.classLoader);
|
||||
this.classReader.accept(visitor, true);
|
||||
this.annotationMetadata = visitor;
|
||||
this.classMetadata = visitor;
|
||||
}
|
||||
return this.annotationMetadata;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,13 +63,22 @@ public class SimpleMetadataReaderFactory implements MetadataReaderFactory {
|
||||
}
|
||||
|
||||
|
||||
public MetadataReader getMetadataReader(String className) throws IOException {
|
||||
/**
|
||||
* Return the ResourceLoader that this MetadataReaderFactory has been
|
||||
* constructed with.
|
||||
*/
|
||||
public final ResourceLoader getResourceLoader() {
|
||||
return this.resourceLoader;
|
||||
}
|
||||
|
||||
|
||||
public SimpleMetadataReader getMetadataReader(String className) throws IOException {
|
||||
String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +
|
||||
ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
|
||||
return getMetadataReader(this.resourceLoader.getResource(resourcePath));
|
||||
}
|
||||
|
||||
public MetadataReader getMetadataReader(Resource resource) throws IOException {
|
||||
public SimpleMetadataReader getMetadataReader(Resource resource) throws IOException {
|
||||
InputStream is = resource.getInputStream();
|
||||
try {
|
||||
return new SimpleMetadataReader(new ClassReader(is), this.resourceLoader.getClassLoader());
|
||||
|
||||
Reference in New Issue
Block a user