From e6f75f212d68d1514951dc8169bcf34b56d9d4af Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 20 Nov 2008 17:33:46 +0000 Subject: [PATCH] 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 --- .../AutowiredAnnotationBeanPostProcessor.java | 47 +++++-- .../annotation/CustomAutowireConfigurer.java | 10 +- .../factory/annotation/InjectionMetadata.java | 4 +- ...erAnnotationAutowireCandidateResolver.java | 38 +++++- .../beans/factory/annotation/Value.java | 29 +++- .../config/ConstructorArgumentValues.java | 48 +++---- .../support/AbstractBeanDefinition.java | 16 +-- .../support/AutowireCandidateResolver.java | 10 +- .../factory/support/ConstructorResolver.java | 39 +++--- .../support/DefaultListableBeanFactory.java | 62 +++++---- .../factory/support/MethodOverrides.java | 33 +++-- .../SimpleAutowireCandidateResolver.java | 10 +- .../annotation/AnnotationConfigUtils.java | 2 +- ...AbstractRefreshableApplicationContext.java | 6 +- .../support/GenericApplicationContext.java | 2 + .../ApplicationContextExpressionTests.java | 128 +++++++++++++++++- .../core/annotation/AnnotationUtils.java | 2 +- .../CustomAutowireConfigurerTests.java | 6 +- 18 files changed, 354 insertions(+), 138 deletions(-) diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 5832b90cdb5..ae889fc7056 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -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 autowiredAnnotationType = Autowired.class; + @SuppressWarnings("unchecked") + private Class[] autowiredAnnotationTypes = + new Class[] {Autowired.class, Qualifier.class, Value.class}; private String requiredParameterName = "required"; private boolean requiredParameterValue = true; - private Class 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 autowiredAnnotationType) { - Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null"); - this.autowiredAnnotationType = autowiredAnnotationType; + public void setAutowiredAnnotationTypes(Class[] 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. + *

The default autowired annotation type is the Spring-provided + * {@link Autowired} annotation. + *

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 getAutowiredAnnotationType() { - return this.autowiredAnnotationType; + @SuppressWarnings("unchecked") + public void setAutowiredAnnotationType(Class 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 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 diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurer.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurer.java index 7baf644e4e9..91bc3b5bfe3 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurer.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurer.java @@ -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; } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java index f26bc1ff655..89b55da372e 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java @@ -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 { diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index f4497ad316e..6b1cbc3ed6f 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java @@ -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> qualifierTypes; + private Class 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. + *

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. *

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 qualifierType) { this.qualifierTypes.add(qualifierType); } + /** + * Set the 'value' annotation type, to be used on fields, method parameters + * and constructor parameters. + *

The default value annotation type is the Spring-provided + * {@link Value} annotation. + *

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 valueAnnotationType) { + this.valueAnnotationType = valueAnnotationType; + } + + /** * Determine if the provided bean definition is an autowire candidate. *

To be considered a candidate the bean's autowire-candidate @@ -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; + } + } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/Value.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/Value.java index beaba9be815..da290eccaf4 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/Value.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/Value.java @@ -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. + * + *

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(); } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java index 94c3cc627d4..49eba911199 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java @@ -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 indexedArgumentValues = new HashMap(); - private final List genericArgumentValues = new LinkedList(); + private final List genericArgumentValues = new LinkedList(); /** @@ -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 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 getIndexedArgumentValues() { return Collections.unmodifiableMap(this.indexedArgumentValues); } @@ -220,8 +218,7 @@ public class ConstructorArgumentValues { * @return the ValueHolder for the argument, or null 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 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 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 entry : this.indexedArgumentValues.entrySet()) { + hashCode = 31 * hashCode + (entry.getValue().contentHashCode() ^ entry.getKey().hashCode()); } return hashCode; } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java index d8a0041186b..93621785b4e 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java @@ -134,7 +134,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess private boolean autowireCandidate = true; - private final Map qualifiers = new LinkedHashMap(); + private final Map qualifiers = + new LinkedHashMap(); 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 getQualifiers() { + return new LinkedHashSet(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); } } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java index 282c60e0716..84aaf252259 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java @@ -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 null if none found + */ + Object getSuggestedValue(DependencyDescriptor descriptor); + } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 87b2a8aa175..2585a913523 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -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 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(); } causes.add(ex); continue; @@ -348,7 +347,7 @@ class ConstructorResolver { minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } - List causes = null; + List 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(); } 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 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 usedValueHolders = + new HashSet(paramTypes.length); + Set autowiredBeanNames = new LinkedHashSet(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 autowiredBeanNames, TypeConverter typeConverter) { return this.autowireFactory.resolveDependency( new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter); diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 2457d1525b1..44530f7117b 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -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 resolvableDependencies = new HashMap(); /** Map of bean definition objects, keyed by bean name */ private final Map beanDefinitionMap = new ConcurrentHashMap(); @@ -96,15 +98,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto /** List of bean definition names, in registration order */ private final List beanDefinitionNames = new ArrayList(); + /** 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 resolvableDependencies = new HashMap(); - /** * 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 null). - */ - 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 null). + */ + 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 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 matchingBeans = findAutowireCandidates(beanName, componentType, descriptor); diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java index 541955a6854..e4ddfcd6ea4 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java @@ -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 overrides = new HashSet(); /** @@ -73,7 +72,7 @@ public class MethodOverrides { * @return Set of MethodOverride objects * @see MethodOverride */ - public Set getOverrides() { + public Set getOverrides() { return this.overrides; } @@ -90,25 +89,25 @@ public class MethodOverrides { * @return the method override, or null 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 diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java index 92830ddf46f..3eb526fd6f2 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java @@ -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 *

To be considered a candidate the bean's autowire-candidate * 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; + } + } diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java index 5c05e22dabe..362b5aad45b 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java @@ -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); diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java index df6f5061bfa..4d76a1d696f 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java +++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java @@ -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()); } /** diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java index 23bfecf00c6..b8fab005bbe 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java +++ b/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java @@ -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()); } /** diff --git a/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java b/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java index 38e82eeaa33..6659be1bc04 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java @@ -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; + } + } + } diff --git a/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 5e38149b444..94af2c95bd2 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/org.springframework.core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -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"; /** diff --git a/org.springframework.testsuite/src/test/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurerTests.java b/org.springframework.testsuite/src/test/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurerTests.java index 22310cc587c..f752db1a842 100644 --- a/org.springframework.testsuite/src/test/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurerTests.java +++ b/org.springframework.testsuite/src/test/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurerTests.java @@ -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; + } } }