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 6b1cbc3ed6f..0f62ce5b4e9 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-2008 the original author or authors.
+ * Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,11 +22,13 @@ import java.util.Map;
import java.util.Set;
import org.springframework.beans.SimpleTypeConverter;
+import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.AutowireCandidateResolver;
+import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -111,7 +113,7 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
/**
- * Determine if the provided bean definition is an autowire candidate.
+ * Determine whether the provided bean definition is an autowire candidate.
*
To be considered a candidate the bean's autowire-candidate
* attribute must not have been set to 'false'. Also if an annotation on
* the field or parameter to be autowired is recognized by this bean factory
@@ -120,68 +122,38 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
* the same qualifier or match by meta attributes. A "value" attribute will
* fallback to match against the bean name or an alias if a qualifier or
* attribute does not match.
+ * @see Qualifier
*/
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
if (!bdHolder.getBeanDefinition().isAutowireCandidate()) {
// if explicitly false, do not proceed with qualifier check
return false;
}
- if (descriptor == null || ObjectUtils.isEmpty(descriptor.getAnnotations())) {
+ if (descriptor == null) {
// no qualification necessary
return true;
}
- AbstractBeanDefinition bd = (AbstractBeanDefinition) bdHolder.getBeanDefinition();
+ boolean match = checkQualifiers(bdHolder, descriptor.getAnnotations());
+ if (match && descriptor.getMethodParameter() != null) {
+ match = checkQualifiers(bdHolder, descriptor.getMethodParameter().getAnnotations());
+ }
+ return match;
+ }
+
+ /**
+ * Match the given qualifier annotations against the candidate bean definition.
+ */
+ protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
+ if (ObjectUtils.isEmpty(annotationsToSearch)) {
+ return true;
+ }
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
- Annotation[] annotations = descriptor.getAnnotations();
- for (Annotation annotation : annotations) {
+ for (Annotation annotation : annotationsToSearch) {
Class extends Annotation> type = annotation.annotationType();
if (isQualifier(type)) {
- AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
- if (qualifier == null) {
- qualifier = bd.getQualifier(ClassUtils.getShortName(type));
- }
- if (qualifier == null && bd.hasBeanClass()) {
- // look for matching annotation on the target class
- Class> beanClass = bd.getBeanClass();
- Annotation targetAnnotation = beanClass.getAnnotation(type);
- if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
- return true;
- }
- }
- Map attributes = AnnotationUtils.getAnnotationAttributes(annotation);
- if (attributes.isEmpty() && qualifier == null) {
- // if no attributes, the qualifier must be present
+ if (!checkQualifier(bdHolder, annotation, typeConverter)) {
return false;
}
- for (Map.Entry entry : attributes.entrySet()) {
- String attributeName = entry.getKey();
- Object expectedValue = entry.getValue();
- Object actualValue = null;
- // check qualifier first
- if (qualifier != null) {
- actualValue = qualifier.getAttribute(attributeName);
- }
- if (actualValue == null) {
- // fall back on bean definition attribute
- actualValue = bd.getAttribute(attributeName);
- }
- if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
- (expectedValue.equals(bdHolder.getBeanName()) ||
- ObjectUtils.containsElement(bdHolder.getAliases(), expectedValue))) {
- // fall back on bean name (or alias) match
- continue;
- }
- if (actualValue == null && qualifier != null) {
- // fall back on default, but only if the qualifier is present
- actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
- }
- if (actualValue != null) {
- actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
- }
- if (!expectedValue.equals(actualValue)) {
- return false;
- }
- }
}
}
return true;
@@ -190,7 +162,7 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
/**
* Checks whether the given annotation type is a recognized qualifier type.
*/
- private boolean isQualifier(Class extends Annotation> annotationType) {
+ protected boolean isQualifier(Class extends Annotation> annotationType) {
for (Class extends Annotation> qualifierType : this.qualifierTypes) {
if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
return true;
@@ -199,8 +171,84 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
return false;
}
+ /**
+ * Match the given qualifier annotation against the candidate bean definition.
+ */
+ protected boolean checkQualifier(
+ BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
+
+ Class extends Annotation> type = annotation.annotationType();
+ AbstractBeanDefinition bd = (AbstractBeanDefinition) bdHolder.getBeanDefinition();
+ AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
+ if (qualifier == null) {
+ qualifier = bd.getQualifier(ClassUtils.getShortName(type));
+ }
+ if (qualifier == null && bd.hasBeanClass()) {
+ // look for matching annotation on the target class
+ Class> beanClass = bd.getBeanClass();
+ Annotation targetAnnotation = beanClass.getAnnotation(type);
+ if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
+ return true;
+ }
+ }
+ Map attributes = AnnotationUtils.getAnnotationAttributes(annotation);
+ if (attributes.isEmpty() && qualifier == null) {
+ // if no attributes, the qualifier must be present
+ return false;
+ }
+ for (Map.Entry entry : attributes.entrySet()) {
+ String attributeName = entry.getKey();
+ Object expectedValue = entry.getValue();
+ Object actualValue = null;
+ // check qualifier first
+ if (qualifier != null) {
+ actualValue = qualifier.getAttribute(attributeName);
+ }
+ if (actualValue == null) {
+ // fall back on bean definition attribute
+ actualValue = bd.getAttribute(attributeName);
+ }
+ if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
+ (expectedValue.equals(bdHolder.getBeanName()) ||
+ ObjectUtils.containsElement(bdHolder.getAliases(), expectedValue))) {
+ // fall back on bean name (or alias) match
+ continue;
+ }
+ if (actualValue == null && qualifier != null) {
+ // fall back on default, but only if the qualifier is present
+ actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
+ }
+ if (actualValue != null) {
+ actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
+ }
+ if (!expectedValue.equals(actualValue)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Determine whether the given dependency carries a value annotation.
+ * @see Value
+ */
public Object getSuggestedValue(DependencyDescriptor descriptor) {
- for (Annotation annotation : descriptor.getAnnotations()) {
+ Object value = findValue(descriptor.getAnnotations());
+ if (value == null) {
+ MethodParameter methodParam = descriptor.getMethodParameter();
+ if (methodParam != null) {
+ value = findValue(methodParam.getAnnotations());
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Determine a suggested value from any of the given candidate annotations.
+ */
+ protected Object findValue(Annotation[] annotationsToSearch) {
+ for (Annotation annotation : annotationsToSearch) {
if (this.valueAnnotationType.isInstance(annotation)) {
Object value = AnnotationUtils.getValue(annotation);
if (value == 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 da290eccaf4..b411c0330d7 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,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import java.lang.annotation.Target;
* @see org.springframework.beans.factory.support.AutowireCandidateResolver#getSuggestedValue
*/
@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD, ElementType.PARAMETER})
+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Value {
/**
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 262a3b08be6..303ff0fcb05 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
@@ -17,6 +17,7 @@
package org.springframework.beans.factory.support;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
@@ -120,24 +121,7 @@ class ConstructorResolver {
// Found a cached constructor...
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
- Class[] paramTypes = constructorToUse.getParameterTypes();
- Object[] argsToResolve = mbd.preparedConstructorArguments;
- TypeConverter converter = (this.typeConverter != null ? this.typeConverter : bw);
- BeanDefinitionValueResolver valueResolver =
- new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
- argsToUse = new Object[argsToResolve.length];
- for (int i = 0; i < argsToResolve.length; i++) {
- Object argValue = argsToResolve[i];
- MethodParameter methodParam = new MethodParameter(constructorToUse, i);
- GenericTypeResolver.resolveParameterType(methodParam, constructorToUse.getDeclaringClass());
- if (argValue instanceof AutowiredArgumentMarker) {
- argValue = resolveAutowiredArgument(methodParam, beanName, null, converter);
- }
- else if (argValue instanceof BeanMetadataElement) {
- argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
- }
- argsToUse[i] = converter.convertIfNecessary(argValue, paramTypes[i], methodParam);
- }
+ argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse);
}
}
}
@@ -317,24 +301,7 @@ class ConstructorResolver {
// Found a cached factory method...
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
- Class[] paramTypes = factoryMethodToUse.getParameterTypes();
- Object[] argsToResolve = mbd.preparedConstructorArguments;
- TypeConverter converter = (this.typeConverter != null ? this.typeConverter : bw);
- BeanDefinitionValueResolver valueResolver =
- new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
- argsToUse = new Object[argsToResolve.length];
- for (int i = 0; i < argsToResolve.length; i++) {
- Object argValue = argsToResolve[i];
- MethodParameter methodParam = new MethodParameter(factoryMethodToUse, i);
- GenericTypeResolver.resolveParameterType(methodParam, factoryClass);
- if (argValue instanceof AutowiredArgumentMarker) {
- argValue = resolveAutowiredArgument(methodParam, beanName, null, converter);
- }
- else if (argValue instanceof BeanMetadataElement) {
- argValue = valueResolver.resolveValueIfNecessary("factory method argument", argValue);
- }
- argsToUse[i] = converter.convertIfNecessary(argValue, paramTypes[i], methodParam);
- }
+ argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse);
}
}
}
@@ -615,6 +582,48 @@ class ConstructorResolver {
return args;
}
+ /**
+ * Resolve the prepared arguments stored in the given bean definition.
+ */
+ private Object[] resolvePreparedArguments(
+ String beanName, RootBeanDefinition mbd, BeanWrapper bw, Member methodOrCtor) {
+
+ Class[] paramTypes = (methodOrCtor instanceof Method ?
+ ((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes());
+ Object[] argsToResolve = mbd.preparedConstructorArguments;
+ TypeConverter converter = (this.typeConverter != null ? this.typeConverter : bw);
+ BeanDefinitionValueResolver valueResolver =
+ new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
+ Object[] resolvedArgs = new Object[argsToResolve.length];
+ for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
+ Object argValue = argsToResolve[argIndex];
+ MethodParameter methodParam = MethodParameter.forMethodOrConstructor(methodOrCtor, argIndex);
+ GenericTypeResolver.resolveParameterType(methodParam, methodOrCtor.getDeclaringClass());
+ if (argValue instanceof AutowiredArgumentMarker) {
+ argValue = resolveAutowiredArgument(methodParam, beanName, null, converter);
+ }
+ else if (argValue instanceof BeanMetadataElement) {
+ argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
+ }
+ else if (argValue instanceof String) {
+ argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
+ }
+ Class paramType = paramTypes[argIndex];
+ try {
+ resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
+ }
+ catch (TypeMismatchException ex) {
+ String methodType = (methodOrCtor instanceof Constructor ? "constructor" : "factory method");
+ throw new UnsatisfiedDependencyException(
+ mbd.getResourceDescription(), beanName, argIndex, paramType,
+ "Could not convert " + methodType + " argument value of type [" +
+ ObjectUtils.nullSafeClassName(argValue) +
+ "] to required type [" + paramType.getName() + "]: " + ex.getMessage());
+ }
+ }
+ return resolvedArgs;
+ }
+
/**
* Template method for resolving the specified argument which is supposed to be autowired.
*/
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 6faf23bd962..e8266799719 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
@@ -106,6 +106,11 @@ public class ApplicationContextExpressionTests {
bd5.setScope("myScope");
ac.registerBeanDefinition("tb5", bd5);
+ GenericBeanDefinition bd6 = new GenericBeanDefinition();
+ bd6.setBeanClass(PropertyValueTestBean.class);
+ bd6.setScope("myScope");
+ ac.registerBeanDefinition("tb6", bd6);
+
System.getProperties().put("country", "UK");
try {
ac.refresh();
@@ -138,6 +143,12 @@ public class ApplicationContextExpressionTests {
assertEquals(42, tb5.age);
assertEquals("UK", tb5.country);
assertSame(tb0, tb5.tb);
+
+ PropertyValueTestBean tb6 = ac.getBean("tb6", PropertyValueTestBean.class);
+ assertEquals("XXXmyNameYYY42ZZZ", tb6.name);
+ assertEquals(42, tb6.age);
+ assertEquals("UK", tb6.country);
+ assertSame(tb0, tb6.tb);
}
finally {
System.getProperties().remove("country");
@@ -153,12 +164,13 @@ public class ApplicationContextExpressionTests {
GenericApplicationContext ac = new GenericApplicationContext();
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
- rbd.getPropertyValues().addPropertyValue("name", "juergen");
+ rbd.getConstructorArgumentValues().addGenericArgumentValue("#{systemProperties.name}");
rbd.getPropertyValues().addPropertyValue("country", "#{systemProperties.country}");
ac.registerBeanDefinition("test", rbd);
ac.refresh();
StopWatch sw = new StopWatch();
sw.start("prototype");
+ System.getProperties().put("name", "juergen");
System.getProperties().put("country", "UK");
try {
for (int i = 0; i < 100000; i++) {
@@ -170,6 +182,7 @@ public class ApplicationContextExpressionTests {
}
finally {
System.getProperties().remove("country");
+ System.getProperties().remove("name");
}
System.out.println(sw.getTotalTimeMillis());
assertTrue("Prototype creation took too long: " + sw.getTotalTimeMillis(), sw.getTotalTimeMillis() < 6000);
@@ -230,7 +243,7 @@ public class ApplicationContextExpressionTests {
public void configure(
@Qualifier("original") TestBean tb,
@Value("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ") String name,
- @Value("#{mySpecialAttr}")int age,
+ @Value("#{mySpecialAttr}") int age,
@Value("#{systemProperties.country}") String country) {
this.name = name;
this.age = age;
@@ -239,4 +252,36 @@ public class ApplicationContextExpressionTests {
}
}
+
+ public static class PropertyValueTestBean {
+
+ public String name;
+
+ public int age;
+
+ public String country;
+
+ public TestBean tb;
+
+ @Value("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ")
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Value("#{mySpecialAttr}")
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ @Value("#{systemProperties.country}")
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ @Qualifier("original")
+ public void setTb(TestBean tb) {
+ this.tb = tb;
+ }
+ }
+
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/MethodParameter.java b/org.springframework.core/src/main/java/org/springframework/core/MethodParameter.java
index 17607e6ec2c..13c9841127d 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/MethodParameter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/MethodParameter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,8 @@ package org.springframework.core;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
-import java.lang.reflect.TypeVariable;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
@@ -177,7 +177,14 @@ public class MethodParameter {
}
/**
- * Return the annotations associated with the method/constructor parameter.
+ * Return the annotations associated with the target method/constructor itself.
+ */
+ public Annotation[] getAnnotations() {
+ return (this.method != null ? this.method.getAnnotations() : this.constructor.getAnnotations());
+ }
+
+ /**
+ * Return the annotations associated with the specific method/constructor parameter.
*/
public Annotation[] getParameterAnnotations() {
if (this.parameterAnnotations == null) {