avoid caching for expression results even when specified as TypedStringValue (SPR-6115)

This commit is contained in:
Juergen Hoeller 2009-09-17 10:04:36 +00:00
parent 8bf1ee5e7c
commit 160521074b
5 changed files with 109 additions and 6 deletions

View File

@ -44,6 +44,8 @@ public class TypedStringValue implements BeanMetadataElement {
private String specifiedTypeName; private String specifiedTypeName;
private volatile boolean dynamic;
/** /**
* Create a new {@link TypedStringValue} for the given String value. * Create a new {@link TypedStringValue} for the given String value.
@ -185,6 +187,21 @@ public class TypedStringValue implements BeanMetadataElement {
return this.specifiedTypeName; return this.specifiedTypeName;
} }
/**
* Mark this value as dynamic, i.e. as containing an expression
* and hence not being subject to caching.
*/
public void setDynamic() {
this.dynamic = true;
}
/**
* Return whether this value has been marked as dynamic.
*/
public boolean isDynamic() {
return this.dynamic;
}
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {

View File

@ -1311,7 +1311,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
} }
deepCopy.add(pv); deepCopy.add(pv);
} }
else if (originalValue instanceof TypedStringValue && convertible && else if (convertible && originalValue instanceof TypedStringValue &&
!((TypedStringValue) originalValue).isDynamic() &&
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) { !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue); pv.setConvertedValue(convertedValue);
deepCopy.add(pv); deepCopy.add(pv);

View File

@ -167,10 +167,10 @@ class BeanDefinitionValueResolver {
Object propKey = propEntry.getKey(); Object propKey = propEntry.getKey();
Object propValue = propEntry.getValue(); Object propValue = propEntry.getValue();
if (propKey instanceof TypedStringValue) { if (propKey instanceof TypedStringValue) {
propKey = evaluate(((TypedStringValue) propKey).getValue()); propKey = evaluate((TypedStringValue) propKey);
} }
if (propValue instanceof TypedStringValue) { if (propValue instanceof TypedStringValue) {
propValue = evaluate(((TypedStringValue) propValue).getValue()); propValue = evaluate((TypedStringValue) propValue);
} }
copy.put(propKey, propValue); copy.put(propKey, propValue);
} }
@ -179,7 +179,7 @@ class BeanDefinitionValueResolver {
else if (value instanceof TypedStringValue) { else if (value instanceof TypedStringValue) {
// Convert value to target type here. // Convert value to target type here.
TypedStringValue typedStringValue = (TypedStringValue) value; TypedStringValue typedStringValue = (TypedStringValue) value;
Object valueObject = evaluate(typedStringValue.getValue()); Object valueObject = evaluate(typedStringValue);
try { try {
Class<?> resolvedTargetType = resolveTargetType(typedStringValue); Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
if (resolvedTargetType != null) { if (resolvedTargetType != null) {
@ -201,6 +201,19 @@ class BeanDefinitionValueResolver {
} }
} }
/**
* Evaluate the given value as an expression, if necessary.
* @param value the candidate value (may be an expression)
* @return the resolved value
*/
protected Object evaluate(TypedStringValue value) {
Object result = this.beanFactory.evaluateBeanDefinitionString(value.getValue(), this.beanDefinition);
if (result != value.getValue()) {
value.setDynamic();
}
return result;
}
/** /**
* Evaluate the given value as an expression, if necessary. * Evaluate the given value as an expression, if necessary.
* @param value the candidate value (may be an expression) * @param value the candidate value (may be an expression)

View File

@ -32,6 +32,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter; import org.springframework.expression.spel.support.StandardTypeConverter;
import org.springframework.expression.spel.support.StandardTypeLocator; import org.springframework.expression.spel.support.StandardTypeLocator;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/** /**
* Standard implementation of the * Standard implementation of the
@ -109,6 +110,9 @@ public class StandardBeanExpressionResolver implements BeanExpressionResolver {
public Object evaluate(String value, BeanExpressionContext evalContext) throws BeansException { public Object evaluate(String value, BeanExpressionContext evalContext) throws BeansException {
if (!StringUtils.hasLength(value)) {
return value;
}
try { try {
Expression expr = this.expressionCache.get(value); Expression expr = this.expressionCache.get(value);
if (expr == null) { if (expr == null) {

View File

@ -16,8 +16,8 @@
package org.springframework.context.expression; package org.springframework.context.expression;
import java.util.Properties;
import java.io.Serializable; import java.io.Serializable;
import java.util.Properties;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -31,14 +31,15 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.beans.factory.config.Scope; import org.springframework.beans.factory.config.Scope;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.AutowireCandidateQualifier; import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.StopWatch;
import org.springframework.util.SerializationTestUtils; import org.springframework.util.SerializationTestUtils;
import org.springframework.util.StopWatch;
/** /**
* @author Juergen Hoeller * @author Juergen Hoeller
@ -170,6 +171,38 @@ public class ApplicationContextExpressionTests {
} }
} }
@Test
public void prototypeCreationReevaluatesExpressions() {
GenericApplicationContext ac = new GenericApplicationContext();
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
RootBeanDefinition rbd = new RootBeanDefinition(PrototypeTestBean.class);
rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
rbd.getPropertyValues().addPropertyValue("country", "#{systemProperties.country}");
rbd.getPropertyValues().addPropertyValue("country2", new TypedStringValue("#{systemProperties.country}"));
ac.registerBeanDefinition("test", rbd);
ac.refresh();
try {
System.getProperties().put("name", "juergen1");
System.getProperties().put("country", "UK1");
PrototypeTestBean tb = (PrototypeTestBean) ac.getBean("test");
assertEquals("juergen1", tb.getName());
assertEquals("UK1", tb.getCountry());
assertEquals("UK1", tb.getCountry2());
System.getProperties().put("name", "juergen2");
System.getProperties().put("country", "UK2");
tb = (PrototypeTestBean) ac.getBean("test");
assertEquals("juergen2", tb.getName());
assertEquals("UK2", tb.getCountry());
assertEquals("UK2", tb.getCountry2());
}
finally {
System.getProperties().remove("name");
System.getProperties().remove("country");
}
}
@Test @Test
public void prototypeCreationIsFastEnough() { public void prototypeCreationIsFastEnough() {
if (factoryLog.isTraceEnabled() || factoryLog.isDebugEnabled()) { if (factoryLog.isTraceEnabled() || factoryLog.isDebugEnabled()) {
@ -302,4 +335,39 @@ public class ApplicationContextExpressionTests {
} }
} }
public static class PrototypeTestBean {
public String name;
public String country;
public String country2;
@Value("#{systemProperties.name}")
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setCountry(String country) {
this.country = country;
}
public String getCountry() {
return country;
}
public void setCountry2(String country2) {
this.country2 = country2;
}
public String getCountry2() {
return country2;
}
}
} }