GH-34: Resolve Placeholders in Expressions

Further enhancement to #34
This commit is contained in:
Gary Russell
2016-12-01 10:21:51 -05:00
committed by Dave Syer
parent ba35d97222
commit 757f4ad546
3 changed files with 40 additions and 10 deletions

View File

@@ -298,6 +298,8 @@ public void service3() {
These use the familier Spring SpEL expression syntax (`#{...}`).
Expressions can contain property placeholders such as `#{${max.delay}}` or `#{@exceptionChecker.${retry.method}(#root)}`
- `exceptionExpression` is evaluated against the thrown exception as the `#root` object.
- `maxAttemptsExpression` and the `@BackOff` expression attributes are evaluated once, during initialization; there is no root object for the evaluation but they can reference other beans in the context.

View File

@@ -31,6 +31,7 @@ import org.springframework.aop.IntroductionInterceptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
@@ -288,11 +289,11 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
Integer maxAttempts = (Integer) attrs.get("maxAttempts");
String maxAttemptsExpression = (String) attrs.get("maxAttemptsExpression");
if (StringUtils.hasText(maxAttemptsExpression)) {
maxAttempts = PARSER.parseExpression(maxAttemptsExpression, PARSER_CONTEXT).getValue(this.evaluationContext,
Integer.class);
maxAttempts = PARSER.parseExpression(resolve(maxAttemptsExpression), PARSER_CONTEXT)
.getValue(this.evaluationContext, Integer.class);
}
if (includes.length == 0 && excludes.length == 0) {
SimpleRetryPolicy simple = hasExpression ? new ExpressionRetryPolicy(exceptionExpression)
SimpleRetryPolicy simple = hasExpression ? new ExpressionRetryPolicy(resolve(exceptionExpression))
.withBeanFactory(this.beanFactory)
: new SimpleRetryPolicy();
simple.setMaxAttempts(maxAttempts);
@@ -317,17 +318,17 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
private BackOffPolicy getBackoffPolicy(Backoff backoff) {
long min = backoff.delay() == 0 ? backoff.value() : backoff.delay();
if (StringUtils.hasText(backoff.delayExpression())) {
min = PARSER.parseExpression(backoff.delayExpression(), PARSER_CONTEXT).getValue(this.evaluationContext,
Long.class);
min = PARSER.parseExpression(resolve(backoff.delayExpression()), PARSER_CONTEXT)
.getValue(this.evaluationContext, Long.class);
}
long max = backoff.maxDelay();
if (StringUtils.hasText(backoff.maxDelayExpression())) {
max = PARSER.parseExpression(backoff.maxDelayExpression(), PARSER_CONTEXT).getValue(this.evaluationContext,
Long.class);
max = PARSER.parseExpression(resolve(backoff.maxDelayExpression()), PARSER_CONTEXT)
.getValue(this.evaluationContext, Long.class);
}
double multiplier = backoff.multiplier();
if (StringUtils.hasText(backoff.multiplierExpression())) {
multiplier = PARSER.parseExpression(backoff.multiplierExpression(), PARSER_CONTEXT)
multiplier = PARSER.parseExpression(resolve(backoff.multiplierExpression()), PARSER_CONTEXT)
.getValue(this.evaluationContext, Double.class);
}
if (multiplier > 0) {
@@ -360,4 +361,16 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
return policy;
}
/**
* Resolve the specified value if possible.
*
* @see ConfigurableBeanFactory#resolveEmbeddedValue
*/
private String resolve(String value) {
if (this.beanFactory != null && this.beanFactory instanceof ConfigurableBeanFactory) {
return ((ConfigurableBeanFactory) this.beanFactory).resolveEmbeddedValue(value);
}
return value;
}
}

View File

@@ -24,6 +24,7 @@ import static org.junit.Assert.fail;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Properties;
import org.aopalliance.intercept.MethodInterceptor;
import org.junit.Test;
@@ -33,6 +34,7 @@ import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.backoff.Sleeper;
import org.springframework.retry.interceptor.RetryInterceptorBuilder;
@@ -215,6 +217,18 @@ public class EnableRetryTests {
@EnableRetry
protected static class TestConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer pspc() {
PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
Properties properties = new Properties();
properties.setProperty("one", "1");
properties.setProperty("five", "5");
properties.setProperty("onePointOne", "1.1");
properties.setProperty("retryMethod", "shouldRetry");
pspc.setProperties(properties);
return pspc;
}
@SuppressWarnings("serial")
@Bean
public Sleeper sleeper() {
@@ -447,9 +461,10 @@ public class EnableRetryTests {
throw new RuntimeException("this cannot be retried");
}
@Retryable(exceptionExpression="#{@exceptionChecker.shouldRetry(#root)}",
@Retryable(exceptionExpression="#{@exceptionChecker.${retryMethod}(#root)}",
maxAttemptsExpression = "#{@integerFiveBean}",
backoff = @Backoff(delayExpression = "#{1}", maxDelayExpression = "#{5}", multiplierExpression = "#{1.1}"))
backoff = @Backoff(delayExpression = "#{${one}}", maxDelayExpression = "#{${five}}",
multiplierExpression = "#{${onePointOne}}"))
public void service3() {
if (count++ < 8) {
throw new RuntimeException();