avoid caching for expression results even when specified as TypedStringValue (SPR-6115)
This commit is contained in:
parent
8bf1ee5e7c
commit
160521074b
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue