completed value annotation support; Java 5 code style updates

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@312 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Juergen Hoeller 2008-11-20 17:33:46 +00:00
parent fee0e1b8f3
commit e6f75f212d
18 changed files with 354 additions and 138 deletions

View File

@ -18,6 +18,7 @@ package org.springframework.beans.factory.annotation;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@ -99,14 +100,14 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
protected final Log logger = LogFactory.getLog(AutowiredAnnotationBeanPostProcessor.class);
private Class<? extends Annotation> autowiredAnnotationType = Autowired.class;
@SuppressWarnings("unchecked")
private Class<? extends Annotation>[] autowiredAnnotationTypes =
new Class[] {Autowired.class, Qualifier.class, Value.class};
private String requiredParameterName = "required";
private boolean requiredParameterValue = true;
private Class<? extends Annotation> valueAnnotationType = Value.class;
private int order = Ordered.LOWEST_PRECEDENCE - 2;
private ConfigurableListableBeanFactory beanFactory;
@ -127,16 +128,24 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
* (non-Spring-specific) annotation type to indicate that a member is
* supposed to be autowired.
*/
public void setAutowiredAnnotationType(Class<? extends Annotation> autowiredAnnotationType) {
Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null");
this.autowiredAnnotationType = autowiredAnnotationType;
public void setAutowiredAnnotationTypes(Class<? extends Annotation>[] autowiredAnnotationTypes) {
Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty");
this.autowiredAnnotationTypes = autowiredAnnotationTypes;
}
/**
* Return the 'autowired' annotation type.
* Set the 'autowired' annotation type, to be used on constructors, fields,
* setter methods and arbitrary config methods.
* <p>The default autowired annotation type is the Spring-provided
* {@link Autowired} annotation.
* <p>This setter property exists so that developers can provide their own
* (non-Spring-specific) annotation type to indicate that a member is
* supposed to be autowired.
*/
protected Class<? extends Annotation> getAutowiredAnnotationType() {
return this.autowiredAnnotationType;
@SuppressWarnings("unchecked")
public void setAutowiredAnnotationType(Class<? extends Annotation> autowiredAnnotationType) {
Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null");
this.autowiredAnnotationTypes = new Class[] {autowiredAnnotationType};
}
/**
@ -196,7 +205,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
Constructor requiredConstructor = null;
Constructor defaultConstructor = null;
for (Constructor<?> candidate : rawCandidates) {
Annotation annotation = candidate.getAnnotation(getAutowiredAnnotationType());
Annotation annotation = findAutowiredAnnotation(candidate);
if (annotation != null) {
if (requiredConstructor != null) {
throw new BeanCreationException("Invalid autowire-marked constructor: " + candidate +
@ -212,8 +221,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
if (!candidates.isEmpty()) {
throw new BeanCreationException(
"Invalid autowire-marked constructors: " + candidates +
". Found another constructor with 'required' Autowired annotation: " +
requiredConstructor);
". Found another constructor with 'required' Autowired annotation: " +
requiredConstructor);
}
requiredConstructor = candidate;
}
@ -294,7 +303,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
final InjectionMetadata newMetadata = new InjectionMetadata(clazz);
ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
public void doWith(Field field) {
Annotation annotation = field.getAnnotation(getAutowiredAnnotationType());
Annotation annotation = findAutowiredAnnotation(field);
if (annotation != null) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("Autowired annotation is not supported on static fields");
@ -306,7 +315,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
});
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
Annotation annotation = method.getAnnotation(getAutowiredAnnotationType());
Annotation annotation = findAutowiredAnnotation(method);
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("Autowired annotation is not supported on static methods");
@ -328,6 +337,16 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
return metadata;
}
private Annotation findAutowiredAnnotation(AccessibleObject ao) {
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
Annotation annotation = ao.getAnnotation(type);
if (annotation != null) {
return annotation;
}
}
return null;
}
/**
* Obtain all beans of the given type as autowire candidates.
* @param type the type of the bean

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2008 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.
@ -17,7 +17,6 @@
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Annotation;
import java.util.Iterator;
import java.util.Set;
import org.springframework.beans.BeansException;
@ -83,6 +82,7 @@ public class CustomAutowireConfigurer implements BeanFactoryPostProcessor, BeanC
}
@SuppressWarnings("unchecked")
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.customQualifierTypes != null) {
if (!(beanFactory instanceof DefaultListableBeanFactory)) {
@ -91,14 +91,12 @@ public class CustomAutowireConfigurer implements BeanFactoryPostProcessor, BeanC
}
DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
if (!(dlbf.getAutowireCandidateResolver() instanceof QualifierAnnotationAutowireCandidateResolver)) {
throw new IllegalStateException(
"CustomAutowireConfigurer needs to operate on a QualifierAnnotationAutowireCandidateResolver");
dlbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
}
QualifierAnnotationAutowireCandidateResolver resolver =
(QualifierAnnotationAutowireCandidateResolver) dlbf.getAutowireCandidateResolver();
for (Iterator it = this.customQualifierTypes.iterator(); it.hasNext();) {
for (Object value : this.customQualifierTypes) {
Class customType = null;
Object value = it.next();
if (value instanceof Class) {
customType = (Class) value;
}

View File

@ -181,9 +181,9 @@ public class InjectionMetadata {
}
else {
if (this.skip == null) {
this.skip = Boolean.valueOf(checkPropertySkipping(pvs));
this.skip = checkPropertySkipping(pvs);
}
if (this.skip.booleanValue()) {
if (this.skip) {
return;
}
try {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2008 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.
@ -35,17 +35,21 @@ import org.springframework.util.ObjectUtils;
/**
* {@link AutowireCandidateResolver} implementation that matches bean definition
* qualifiers against qualifier annotations on the field or parameter to be autowired.
* Also supports suggested expression values through a value annotation.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 2.5
* @see AutowireCandidateQualifier
* @see Qualifier
* @see Value
*/
public class QualifierAnnotationAutowireCandidateResolver implements AutowireCandidateResolver {
private final Set<Class<? extends Annotation>> qualifierTypes;
private Class<? extends Annotation> valueAnnotationType = Value.class;
/**
* Create a new QualifierAnnotationAutowireCandidateResolver
@ -80,13 +84,32 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
/**
* Register the given type to be used as a qualifier when autowiring.
* <p>This identifies qualifier annotations for direct use (on fields,
* method parameters and constructor parameters) as well as meta
* annotations that in turn identify actual qualifier annotations.
* <p>This implementation only supports annotations as qualifier types.
* The default is Spring's {@link Qualifier} annotation which serves
* as a qualifier for direct use and also as a meta annotation.
* @param qualifierType the annotation type to register
*/
public void addQualifierType(Class<? extends Annotation> qualifierType) {
this.qualifierTypes.add(qualifierType);
}
/**
* Set the 'value' annotation type, to be used on fields, method parameters
* and constructor parameters.
* <p>The default value annotation type is the Spring-provided
* {@link Value} annotation.
* <p>This setter property exists so that developers can provide their own
* (non-Spring-specific) annotation type to indicate a default value
* expression for a specific argument.
*/
public void setValueAnnotationType(Class<? extends Annotation> valueAnnotationType) {
this.valueAnnotationType = valueAnnotationType;
}
/**
* Determine if the provided bean definition is an autowire candidate.
* <p>To be considered a candidate the bean's <em>autowire-candidate</em>
@ -176,4 +199,17 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
return false;
}
public Object getSuggestedValue(DependencyDescriptor descriptor) {
for (Annotation annotation : descriptor.getAnnotations()) {
if (this.valueAnnotationType.isInstance(annotation)) {
Object value = AnnotationUtils.getValue(annotation);
if (value == null) {
throw new IllegalStateException("Value annotation must have a value attribute");
}
return value;
}
}
return null;
}
}

View File

@ -1,3 +1,19 @@
/*
* Copyright 2002-2008 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.annotation;
import java.lang.annotation.ElementType;
@ -6,13 +22,24 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation at the field or method/constructor parameter level
* that indicates a default value expression for the affected argument.
*
* <p>This is typically used for assigning default field values
* with "#{systemProperties.myProp}" style expressions.
*
* @author Juergen Hoeller
* @since 3.0
* @see org.springframework.beans.factory.config.BeanExpressionResolver
* @see org.springframework.beans.factory.support.AutowireCandidateResolver#getSuggestedValue
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface Value {
/**
* The actual value expression: e.g. "#{systemProperties.myProp}".
*/
String value();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2008 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.
@ -42,9 +42,9 @@ import org.springframework.util.ObjectUtils;
*/
public class ConstructorArgumentValues {
private final Map indexedArgumentValues = new HashMap();
private final Map<Integer, ValueHolder> indexedArgumentValues = new HashMap<Integer, ValueHolder>();
private final List genericArgumentValues = new LinkedList();
private final List<ValueHolder> genericArgumentValues = new LinkedList<ValueHolder>();
/**
@ -61,6 +61,7 @@ public class ConstructorArgumentValues {
addArgumentValues(original);
}
/**
* Copy all given argument values into this object, using separate holder
* instances to keep the values independent from the original object.
@ -70,13 +71,10 @@ public class ConstructorArgumentValues {
*/
public void addArgumentValues(ConstructorArgumentValues other) {
if (other != null) {
for (Iterator it = other.indexedArgumentValues.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
ValueHolder valueHolder = (ValueHolder) entry.getValue();
addOrMergeIndexedArgumentValue(entry.getKey(), valueHolder.copy());
for (Map.Entry<Integer, ValueHolder> entry : other.indexedArgumentValues.entrySet()) {
addOrMergeIndexedArgumentValue(entry.getKey(), entry.getValue().copy());
}
for (Iterator it = other.genericArgumentValues.iterator(); it.hasNext();) {
ValueHolder valueHolder = (ValueHolder) it.next();
for (ValueHolder valueHolder : other.genericArgumentValues) {
if (!this.genericArgumentValues.contains(valueHolder)) {
this.genericArgumentValues.add(valueHolder.copy());
}
@ -112,7 +110,7 @@ public class ConstructorArgumentValues {
public void addIndexedArgumentValue(int index, ValueHolder newValue) {
Assert.isTrue(index >= 0, "Index must not be negative");
Assert.notNull(newValue, "ValueHolder must not be null");
addOrMergeIndexedArgumentValue(new Integer(index), newValue);
addOrMergeIndexedArgumentValue(index, newValue);
}
/**
@ -122,8 +120,8 @@ public class ConstructorArgumentValues {
* @param key the index in the constructor argument list
* @param newValue the argument value in the form of a ValueHolder
*/
private void addOrMergeIndexedArgumentValue(Object key, ValueHolder newValue) {
ValueHolder currentValue = (ValueHolder) this.indexedArgumentValues.get(key);
private void addOrMergeIndexedArgumentValue(Integer key, ValueHolder newValue) {
ValueHolder currentValue = this.indexedArgumentValues.get(key);
if (currentValue != null && newValue.getValue() instanceof Mergeable) {
Mergeable mergeable = (Mergeable) newValue.getValue();
if (mergeable.isMergeEnabled()) {
@ -142,7 +140,7 @@ public class ConstructorArgumentValues {
*/
public ValueHolder getIndexedArgumentValue(int index, Class requiredType) {
Assert.isTrue(index >= 0, "Index must not be negative");
ValueHolder valueHolder = (ValueHolder) this.indexedArgumentValues.get(new Integer(index));
ValueHolder valueHolder = this.indexedArgumentValues.get(index);
if (valueHolder != null) {
if (valueHolder.getType() == null ||
(requiredType != null && requiredType.getName().equals(valueHolder.getType()))) {
@ -157,7 +155,7 @@ public class ConstructorArgumentValues {
* @return unmodifiable Map with Integer index as key and ValueHolder as value
* @see ValueHolder
*/
public Map getIndexedArgumentValues() {
public Map<Integer, ValueHolder> getIndexedArgumentValues() {
return Collections.unmodifiableMap(this.indexedArgumentValues);
}
@ -220,8 +218,7 @@ public class ConstructorArgumentValues {
* @return the ValueHolder for the argument, or <code>null</code> if none found
*/
public ValueHolder getGenericArgumentValue(Class requiredType, Set usedValueHolders) {
for (Iterator it = this.genericArgumentValues.iterator(); it.hasNext();) {
ValueHolder valueHolder = (ValueHolder) it.next();
for (ValueHolder valueHolder : this.genericArgumentValues) {
if (usedValueHolders == null || !usedValueHolders.contains(valueHolder)) {
if (requiredType != null) {
// Check matching type.
@ -250,7 +247,7 @@ public class ConstructorArgumentValues {
* @return unmodifiable List of ValueHolders
* @see ValueHolder
*/
public List getGenericArgumentValues() {
public List<ValueHolder> getGenericArgumentValues() {
return Collections.unmodifiableList(this.genericArgumentValues);
}
@ -334,10 +331,9 @@ public class ConstructorArgumentValues {
return false;
}
}
for (Iterator it = this.indexedArgumentValues.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
ValueHolder vh1 = (ValueHolder) entry.getValue();
ValueHolder vh2 = (ValueHolder) that.indexedArgumentValues.get(entry.getKey());
for (Map.Entry<Integer, ValueHolder> entry : this.indexedArgumentValues.entrySet()) {
ValueHolder vh1 = entry.getValue();
ValueHolder vh2 = that.indexedArgumentValues.get(entry.getKey());
if (!vh1.contentEquals(vh2)) {
return false;
}
@ -348,16 +344,12 @@ public class ConstructorArgumentValues {
@Override
public int hashCode() {
int hashCode = 7;
for (Iterator it = this.genericArgumentValues.iterator(); it.hasNext();) {
ValueHolder valueHolder = (ValueHolder) it.next();
for (ValueHolder valueHolder : this.genericArgumentValues) {
hashCode = 31 * hashCode + valueHolder.contentHashCode();
}
hashCode = 29 * hashCode;
for (Iterator it = this.indexedArgumentValues.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
Integer key = (Integer) entry.getKey();
ValueHolder value = (ValueHolder) entry.getValue();
hashCode = 31 * hashCode + (value.contentHashCode() ^ key.hashCode());
for (Map.Entry<Integer, ValueHolder> entry : this.indexedArgumentValues.entrySet()) {
hashCode = 31 * hashCode + (entry.getValue().contentHashCode() ^ entry.getKey().hashCode());
}
return hashCode;
}

View File

@ -134,7 +134,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
private boolean autowireCandidate = true;
private final Map qualifiers = new LinkedHashMap();
private final Map<String, AutowireCandidateQualifier> qualifiers =
new LinkedHashMap<String, AutowireCandidateQualifier>();
private boolean primary = false;
@ -521,8 +522,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
// If it has a no-arg constructor it's deemed to be setter autowiring,
// otherwise we'll try constructor autowiring.
Constructor[] constructors = getBeanClass().getConstructors();
for (int i = 0; i < constructors.length; i++) {
if (constructors[i].getParameterTypes().length == 0) {
for (Constructor constructor : constructors) {
if (constructor.getParameterTypes().length == 0) {
return AUTOWIRE_BY_TYPE;
}
}
@ -605,15 +606,15 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
* Return the qualifier mapped to the provided type name.
*/
public AutowireCandidateQualifier getQualifier(String typeName) {
return (AutowireCandidateQualifier) this.qualifiers.get(typeName);
return this.qualifiers.get(typeName);
}
/**
* Return all registered qualifiers.
* @return the Set of {@link AutowireCandidateQualifier} objects.
*/
public Set getQualifiers() {
return new LinkedHashSet(this.qualifiers.values());
public Set<AutowireCandidateQualifier> getQualifiers() {
return new LinkedHashSet<AutowireCandidateQualifier>(this.qualifiers.values());
}
/**
@ -885,8 +886,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
// Check that lookup methods exists.
MethodOverrides methodOverrides = getMethodOverrides();
if (!methodOverrides.isEmpty()) {
for (Iterator it = methodOverrides.getOverrides().iterator(); it.hasNext(); ) {
MethodOverride mo = (MethodOverride) it.next();
for (MethodOverride mo : methodOverrides.getOverrides()) {
prepareMethodOverride(mo);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2008 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.
@ -38,4 +38,12 @@ public interface AutowireCandidateResolver {
*/
boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor);
/**
* Determine whether a default value is suggested for the given dependency.
* @param descriptor the descriptor for the target method parameter or field
* @return the value suggested (typically an expression String),
* or <code>null</code> if none found
*/
Object getSuggestedValue(DependencyDescriptor descriptor);
}

View File

@ -20,7 +20,6 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
@ -182,7 +181,7 @@ class ConstructorResolver {
}
ArgumentsHolder args = null;
List causes = null;
List<Exception> causes = null;
if (resolvedValues != null) {
// Try to resolve arguments for current constructor.
@ -197,8 +196,8 @@ class ConstructorResolver {
}
if (i == candidates.length - 1 && constructorToUse == null) {
if (causes != null) {
for (Iterator it = causes.iterator(); it.hasNext();) {
this.beanFactory.onSuppressedException((Exception) it.next());
for (Exception cause : causes) {
this.beanFactory.onSuppressedException(cause);
}
}
throw ex;
@ -206,7 +205,7 @@ class ConstructorResolver {
else {
// Swallow and try next constructor.
if (causes == null) {
causes = new LinkedList();
causes = new LinkedList<Exception>();
}
causes.add(ex);
continue;
@ -348,7 +347,7 @@ class ConstructorResolver {
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
List causes = null;
List<Exception> causes = null;
for (int i = 0; i < candidates.length; i++) {
Method candidate = candidates[i];
@ -373,8 +372,8 @@ class ConstructorResolver {
}
if (i == candidates.length - 1 && factoryMethodToUse == null) {
if (causes != null) {
for (Iterator it = causes.iterator(); it.hasNext();) {
this.beanFactory.onSuppressedException((Exception) it.next());
for (Exception cause : causes) {
this.beanFactory.onSuppressedException(cause);
}
}
throw ex;
@ -382,7 +381,7 @@ class ConstructorResolver {
else {
// Swallow and try next overloaded factory method.
if (causes == null) {
causes = new LinkedList();
causes = new LinkedList<Exception>();
}
causes.add(ex);
continue;
@ -455,9 +454,8 @@ class ConstructorResolver {
int minNrOfArgs = cargs.getArgumentCount();
for (Iterator it = cargs.getIndexedArgumentValues().entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
int index = ((Integer) entry.getKey()).intValue();
for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) {
int index = entry.getKey();
if (index < 0) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Invalid constructor argument index: " + index);
@ -465,8 +463,7 @@ class ConstructorResolver {
if (index > minNrOfArgs) {
minNrOfArgs = index + 1;
}
ConstructorArgumentValues.ValueHolder valueHolder =
(ConstructorArgumentValues.ValueHolder) entry.getValue();
ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue();
if (valueHolder.isConverted()) {
resolvedValues.addIndexedArgumentValue(index, valueHolder);
}
@ -480,9 +477,7 @@ class ConstructorResolver {
}
}
for (Iterator it = cargs.getGenericArgumentValues().iterator(); it.hasNext();) {
ConstructorArgumentValues.ValueHolder valueHolder =
(ConstructorArgumentValues.ValueHolder) it.next();
for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) {
if (valueHolder.isConverted()) {
resolvedValues.addGenericArgumentValue(valueHolder);
}
@ -512,8 +507,9 @@ class ConstructorResolver {
TypeConverter converter = (this.typeConverter != null ? this.typeConverter : bw);
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
Set usedValueHolders = new HashSet(paramTypes.length);
Set autowiredBeanNames = new LinkedHashSet(4);
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders =
new HashSet<ConstructorArgumentValues.ValueHolder>(paramTypes.length);
Set<String> autowiredBeanNames = new LinkedHashSet<String>(4);
boolean resolveNecessary = false;
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
@ -589,8 +585,7 @@ class ConstructorResolver {
}
}
for (Iterator it = autowiredBeanNames.iterator(); it.hasNext();) {
String autowiredBeanName = (String) it.next();
for (String autowiredBeanName : autowiredBeanNames) {
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
if (this.beanFactory.logger.isDebugEnabled()) {
this.beanFactory.logger.debug("Autowiring by type from bean name '" + beanName +
@ -612,7 +607,7 @@ class ConstructorResolver {
* Template method for resolving the specified argument which is supposed to be autowired.
*/
protected Object resolveAutowiredArgument(
MethodParameter param, String beanName, Set autowiredBeanNames, TypeConverter typeConverter) {
MethodParameter param, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) {
return this.autowireFactory.resolveDependency(
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);

View File

@ -38,7 +38,6 @@ import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
@ -87,8 +86,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
/** Whether to allow eager class loading even for lazy-init beans */
private boolean allowEagerClassLoading = true;
/** Whether bean definition metadata may be cached for all beans */
private boolean configurationFrozen = false;
/** Resolver to use for checking if a bean definition is an autowire candidate */
private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
/** Map from dependency type to corresponding autowired value */
private final Map<Class, Object> resolvableDependencies = new HashMap<Class, Object>();
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
@ -96,15 +98,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
/** List of bean definition names, in registration order */
private final List<String> beanDefinitionNames = new ArrayList<String>();
/** Whether bean definition metadata may be cached for all beans */
private boolean configurationFrozen = false;
/** Cached array of bean definition names in case of frozen configuration */
private String[] frozenBeanDefinitionNames;
/** Resolver to use for checking if a bean definition is an autowire candidate */
private AutowireCandidateResolver autowireCandidateResolver = new QualifierAnnotationAutowireCandidateResolver();
/** Map from dependency type to corresponding autowired value */
private final Map<Class, Object> resolvableDependencies = new HashMap<Class, Object>();
/**
* Create a new DefaultListableBeanFactory.
@ -122,23 +121,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
/**
* Set a custom autowire candidate resolver for this BeanFactory to use
* when deciding whether a bean definition should be considered as a
* candidate for autowiring.
*/
public void setAutowireCandidateResolver(AutowireCandidateResolver autowireCandidateResolver) {
Assert.notNull(autowireCandidateResolver, "AutowireCandidateResolver must not be null");
this.autowireCandidateResolver = autowireCandidateResolver;
}
/**
* Return the autowire candidate resolver for this BeanFactory (never <code>null</code>).
*/
public AutowireCandidateResolver getAutowireCandidateResolver() {
return this.autowireCandidateResolver;
}
/**
* Set whether it should be allowed to override bean definitions by registering
* a different definition with the same name, automatically replacing the former.
@ -164,6 +146,23 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
this.allowEagerClassLoading = allowEagerClassLoading;
}
/**
* Set a custom autowire candidate resolver for this BeanFactory to use
* when deciding whether a bean definition should be considered as a
* candidate for autowiring.
*/
public void setAutowireCandidateResolver(AutowireCandidateResolver autowireCandidateResolver) {
Assert.notNull(autowireCandidateResolver, "AutowireCandidateResolver must not be null");
this.autowireCandidateResolver = autowireCandidateResolver;
}
/**
* Return the autowire candidate resolver for this BeanFactory (never <code>null</code>).
*/
public AutowireCandidateResolver getAutowireCandidateResolver() {
return this.autowireCandidateResolver;
}
@Override
public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
@ -172,6 +171,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
DefaultListableBeanFactory otherListableFactory = (DefaultListableBeanFactory) otherFactory;
this.allowBeanDefinitionOverriding = otherListableFactory.allowBeanDefinitionOverriding;
this.allowEagerClassLoading = otherListableFactory.allowEagerClassLoading;
this.autowireCandidateResolver = otherListableFactory.autowireCandidateResolver;
this.resolvableDependencies.putAll(otherListableFactory.resolvableDependencies);
}
}
@ -542,6 +543,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
Class type = descriptor.getDependencyType();
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
value = evaluateBeanDefinitionString((String) value, getMergedBeanDefinition(beanName));
}
return typeConverter.convertIfNecessary(value, type);
}
if (type.isArray()) {
Class componentType = type.getComponentType();
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, descriptor);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2008 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.
@ -18,7 +18,6 @@ package org.springframework.beans.factory.support;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
@ -35,7 +34,7 @@ import java.util.Set;
*/
public class MethodOverrides {
private final Set overrides = new HashSet();
private final Set<MethodOverride> overrides = new HashSet<MethodOverride>();
/**
@ -73,7 +72,7 @@ public class MethodOverrides {
* @return Set of MethodOverride objects
* @see MethodOverride
*/
public Set getOverrides() {
public Set<MethodOverride> getOverrides() {
return this.overrides;
}
@ -90,25 +89,25 @@ public class MethodOverrides {
* @return the method override, or <code>null</code> if none
*/
public MethodOverride getOverride(Method method) {
for (Iterator it = this.overrides.iterator(); it.hasNext();) {
MethodOverride methodOverride = (MethodOverride) it.next();
if (methodOverride.matches(method)) {
return methodOverride;
}
for (MethodOverride override : this.overrides) {
if (override.matches(method)) {
return override;
}
}
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof MethodOverrides)) {
return false;
}
MethodOverrides that = (MethodOverrides) other;
return this.overrides.equals(that.overrides);
MethodOverrides that = (MethodOverrides) o;
if (!this.overrides.equals(that.overrides)) return false;
return true;
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2008 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.
@ -36,10 +36,12 @@ public class SimpleAutowireCandidateResolver implements AutowireCandidateResolve
* <p>To be considered a candidate the bean's <em>autowire-candidate</em>
* attribute must not have been set to 'false'.
*/
public boolean isAutowireCandidate(
BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
return bdHolder.getBeanDefinition().isAutowireCandidate();
}
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
}

View File

@ -146,7 +146,7 @@ public class AnnotationConfigUtils {
BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
// Default infrastructure bean: lowest order value; role infrastructure.
definition.getPropertyValues().addPropertyValue("order", new Integer(Ordered.LOWEST_PRECEDENCE));
definition.getPropertyValues().addPropertyValue("order", Ordered.LOWEST_PRECEDENCE);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(beanName, definition);

View File

@ -19,6 +19,7 @@ package org.springframework.context.support;
import java.io.IOException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
@ -195,11 +196,12 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
*/
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding.booleanValue());
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences.booleanValue());
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
}
/**

View File

@ -20,6 +20,7 @@ import java.io.IOException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@ -97,6 +98,7 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
*/
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
this.beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
}
/**

View File

@ -20,8 +20,13 @@ import junit.framework.TestCase;
import org.springframework.beans.TestBean;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.Scope;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
/**
@ -31,6 +36,8 @@ public class ApplicationContextExpressionTests extends TestCase {
public void testGenericApplicationContext() {
GenericApplicationContext ac = new GenericApplicationContext();
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
ac.getBeanFactory().registerScope("myScope", new Scope() {
public Object get(String name, ObjectFactory objectFactory) {
return objectFactory.getObject();
@ -52,28 +59,143 @@ public class ApplicationContextExpressionTests extends TestCase {
return null;
}
});
GenericBeanDefinition bd0 = new GenericBeanDefinition();
bd0.setBeanClass(TestBean.class);
bd0.getPropertyValues().addPropertyValue("name", "myName");
bd0.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "original"));
ac.registerBeanDefinition("tb0", bd0);
GenericBeanDefinition bd1 = new GenericBeanDefinition();
bd1.setBeanClass(TestBean.class);
bd1.getPropertyValues().addPropertyValue("name", "myName");
bd1.setScope("myScope");
bd1.getConstructorArgumentValues().addGenericArgumentValue("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ");
bd1.getConstructorArgumentValues().addGenericArgumentValue("#{mySpecialAttr}");
ac.registerBeanDefinition("tb1", bd1);
GenericBeanDefinition bd2 = new GenericBeanDefinition();
bd2.setBeanClass(TestBean.class);
bd2.setScope("myScope");
bd2.getPropertyValues().addPropertyValue("name", "XXX#{tb1.name}YYY#{mySpecialAttr}ZZZ");
bd2.getPropertyValues().addPropertyValue("name", "XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ");
bd2.getPropertyValues().addPropertyValue("age", "#{mySpecialAttr}");
bd2.getPropertyValues().addPropertyValue("country", "#{systemProperties.country}");
ac.registerBeanDefinition("tb2", bd2);
GenericBeanDefinition bd3 = new GenericBeanDefinition();
bd3.setBeanClass(ValueTestBean.class);
bd3.setScope("myScope");
ac.registerBeanDefinition("tb3", bd3);
GenericBeanDefinition bd4 = new GenericBeanDefinition();
bd4.setBeanClass(ConstructorValueTestBean.class);
bd4.setScope("myScope");
ac.registerBeanDefinition("tb4", bd4);
GenericBeanDefinition bd5 = new GenericBeanDefinition();
bd5.setBeanClass(MethodValueTestBean.class);
bd5.setScope("myScope");
ac.registerBeanDefinition("tb5", bd5);
System.getProperties().put("country", "UK");
try {
ac.refresh();
TestBean tb2 = (TestBean) ac.getBean("tb2");
TestBean tb0 = ac.getBean("tb0", TestBean.class);
TestBean tb1 = ac.getBean("tb1", TestBean.class);
assertEquals("XXXmyNameYYY42ZZZ", tb1.getName());
assertEquals(42, tb1.getAge());
TestBean tb2 = ac.getBean("tb2", TestBean.class);
assertEquals("XXXmyNameYYY42ZZZ", tb2.getName());
assertEquals(42, tb2.getAge());
assertEquals("UK", tb2.getCountry());
ValueTestBean tb3 = ac.getBean("tb3", ValueTestBean.class);
assertEquals("XXXmyNameYYY42ZZZ", tb3.name);
assertEquals(42, tb3.age);
assertEquals("UK", tb3.country);
assertSame(tb0, tb3.tb);
ConstructorValueTestBean tb4 = ac.getBean("tb4", ConstructorValueTestBean.class);
assertEquals("XXXmyNameYYY42ZZZ", tb4.name);
assertEquals(42, tb4.age);
assertEquals("UK", tb4.country);
assertSame(tb0, tb4.tb);
MethodValueTestBean tb5 = ac.getBean("tb5", MethodValueTestBean.class);
assertEquals("XXXmyNameYYY42ZZZ", tb5.name);
assertEquals(42, tb5.age);
assertEquals("UK", tb5.country);
assertSame(tb0, tb5.tb);
}
finally {
System.getProperties().remove("country");
}
}
public static class ValueTestBean {
@Autowired @Value("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ")
public String name;
@Autowired @Value("#{mySpecialAttr}")
public int age;
@Value("#{systemProperties.country}")
public String country;
@Qualifier("original")
public TestBean tb;
}
public static class ConstructorValueTestBean {
public String name;
public int age;
public String country;
public TestBean tb;
@Autowired
public ConstructorValueTestBean(
@Value("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ") String name,
@Value("#{mySpecialAttr}")int age,
@Qualifier("original") TestBean tb,
@Value("#{systemProperties.country}") String country) {
this.name = name;
this.age = age;
this.country = country;
this.tb = tb;
}
}
public static class MethodValueTestBean {
public String name;
public int age;
public String country;
public TestBean tb;
@Autowired
public void configure(
@Qualifier("original") TestBean tb,
@Value("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ") String name,
@Value("#{mySpecialAttr}")int age,
@Value("#{systemProperties.country}") String country) {
this.name = name;
this.age = age;
this.country = country;
this.tb = tb;
}
}
}

View File

@ -51,7 +51,7 @@ import org.springframework.util.Assert;
public abstract class AnnotationUtils {
/** The attribute name for annotations with a single element */
static final String VALUE = "value";
static final String VALUE = "value";
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2008 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.
@ -76,6 +76,10 @@ public class CustomAutowireConfigurerTests extends TestCase {
}
return false;
}
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
}
}