40
40
import org .springframework .core .annotation .AnnotatedElementUtils ;
41
41
import org .springframework .core .annotation .AnnotationAwareOrderComparator ;
42
42
import org .springframework .core .annotation .AnnotationUtils ;
43
+ import org .springframework .expression .Expression ;
43
44
import org .springframework .expression .common .TemplateParserContext ;
44
45
import org .springframework .expression .spel .standard .SpelExpressionParser ;
45
46
import org .springframework .expression .spel .support .StandardEvaluationContext ;
47
+ import org .springframework .retry .RetryContext ;
46
48
import org .springframework .retry .RetryListener ;
47
49
import org .springframework .retry .RetryPolicy ;
48
50
import org .springframework .retry .backoff .BackOffPolicy ;
59
61
import org .springframework .retry .policy .MapRetryContextCache ;
60
62
import org .springframework .retry .policy .RetryContextCache ;
61
63
import org .springframework .retry .policy .SimpleRetryPolicy ;
64
+ import org .springframework .retry .support .Args ;
65
+ import org .springframework .retry .support .RetrySynchronizationManager ;
62
66
import org .springframework .retry .support .RetryTemplate ;
63
67
import org .springframework .util .ConcurrentReferenceHashMap ;
64
68
import org .springframework .util .ReflectionUtils ;
@@ -228,8 +232,8 @@ private MethodInterceptor getStatefulInterceptor(Object target, Method method, R
228
232
if (circuit != null ) {
229
233
RetryPolicy policy = getRetryPolicy (circuit );
230
234
CircuitBreakerRetryPolicy breaker = new CircuitBreakerRetryPolicy (policy );
231
- breaker . setOpenTimeout ( getOpenTimeout ( circuit ) );
232
- breaker . setResetTimeout ( getResetTimeout ( circuit ) );
235
+ openTimeout ( breaker , circuit );
236
+ resetTimeout ( breaker , circuit );
233
237
template .setRetryPolicy (breaker );
234
238
template .setBackOffPolicy (new NoBackOffPolicy ());
235
239
String label = circuit .label ();
@@ -248,45 +252,39 @@ private MethodInterceptor getStatefulInterceptor(Object target, Method method, R
248
252
.recoverer (getRecoverer (target , method )).build ();
249
253
}
250
254
251
- private long getOpenTimeout ( CircuitBreaker circuit ) {
255
+ private void openTimeout ( CircuitBreakerRetryPolicy breaker , CircuitBreaker circuit ) {
252
256
if (StringUtils .hasText (circuit .openTimeoutExpression ())) {
253
- Long value = null ;
254
- if (isTemplate (circuit .openTimeoutExpression ())) {
255
- value = PARSER .parseExpression (resolve (circuit .openTimeoutExpression ()), PARSER_CONTEXT )
256
- .getValue (this .evaluationContext , Long .class );
257
+ Expression parsed = parse (circuit .openTimeoutExpression ());
258
+ if (Evaluation .INITIALIZATION .equals (circuit .expressionEvaluation ())) {
259
+ Long value = parsed .getValue (this .evaluationContext , Long .class );
260
+ if (value != null ) {
261
+ breaker .setOpenTimeout (value );
262
+ return ;
263
+ }
257
264
}
258
265
else {
259
- value = PARSER .parseExpression (resolve (circuit .openTimeoutExpression ()))
260
- .getValue (this .evaluationContext , Long .class );
261
- }
262
- if (value != null ) {
263
- return value ;
266
+ breaker .setOpenTimeout (() -> evaluate (parsed , Long .class ));
267
+ return ;
264
268
}
265
269
}
266
- return circuit .openTimeout ();
270
+ breaker . setOpenTimeout ( circuit .openTimeout () );
267
271
}
268
272
269
- private long getResetTimeout ( CircuitBreaker circuit ) {
273
+ private void resetTimeout ( CircuitBreakerRetryPolicy breaker , CircuitBreaker circuit ) {
270
274
if (StringUtils .hasText (circuit .resetTimeoutExpression ())) {
271
- Long value = null ;
272
- if (isTemplate (circuit .openTimeoutExpression ())) {
273
- value = PARSER .parseExpression (resolve (circuit .resetTimeoutExpression ()), PARSER_CONTEXT )
274
- .getValue (this .evaluationContext , Long .class );
275
+ Expression parsed = parse (circuit .resetTimeoutExpression ());
276
+ if (Evaluation .INITIALIZATION .equals (circuit .expressionEvaluation ())) {
277
+ Long value = parsed .getValue (this .evaluationContext , Long .class );
278
+ if (value != null ) {
279
+ breaker .setResetTimeout (value );
280
+ return ;
281
+ }
275
282
}
276
283
else {
277
- value = PARSER .parseExpression (resolve (circuit .resetTimeoutExpression ()))
278
- .getValue (this .evaluationContext , Long .class );
279
- }
280
- if (value != null ) {
281
- return value ;
284
+ breaker .setResetTimeout (() -> evaluate (parsed , Long .class ));
282
285
}
283
286
}
284
- return circuit .resetTimeout ();
285
- }
286
-
287
- private boolean isTemplate (String expression ) {
288
- return expression .contains (PARSER_CONTEXT .getExpressionPrefix ())
289
- && expression .contains (PARSER_CONTEXT .getExpressionSuffix ());
287
+ breaker .setResetTimeout (circuit .resetTimeout ());
290
288
}
291
289
292
290
private RetryTemplate createTemplate (String [] listenersBeanNames ) {
@@ -340,21 +338,24 @@ private RetryPolicy getRetryPolicy(Annotation retryable) {
340
338
Class <? extends Throwable >[] excludes = (Class <? extends Throwable >[]) attrs .get ("exclude" );
341
339
Integer maxAttempts = (Integer ) attrs .get ("maxAttempts" );
342
340
String maxAttemptsExpression = (String ) attrs .get ("maxAttemptsExpression" );
341
+ Expression parsedExpression = null ;
343
342
if (StringUtils .hasText (maxAttemptsExpression )) {
344
- if (ExpressionRetryPolicy .isTemplate (maxAttemptsExpression )) {
345
- maxAttempts = PARSER .parseExpression (resolve (maxAttemptsExpression ), PARSER_CONTEXT )
346
- .getValue (this .evaluationContext , Integer .class );
347
- }
348
- else {
349
- maxAttempts = PARSER .parseExpression (resolve (maxAttemptsExpression )).getValue (this .evaluationContext ,
350
- Integer .class );
343
+ parsedExpression = parse (maxAttemptsExpression );
344
+ if (Evaluation .INITIALIZATION .equals (attrs .get ("expressionEvaluation" ))) {
345
+ maxAttempts = parsedExpression .getValue (this .evaluationContext , Integer .class );
351
346
}
352
347
}
348
+ final Expression expression = parsedExpression ;
353
349
if (includes .length == 0 && excludes .length == 0 ) {
354
350
SimpleRetryPolicy simple = hasExpression
355
351
? new ExpressionRetryPolicy (resolve (exceptionExpression )).withBeanFactory (this .beanFactory )
356
352
: new SimpleRetryPolicy ();
357
- simple .setMaxAttempts (maxAttempts );
353
+ if (Evaluation .RUNTIME .equals (attrs .get ("expressionEvaluation" ))) {
354
+ simple .setMaxAttempts (() -> evaluate (expression , Integer .class ));
355
+ }
356
+ else {
357
+ simple .setMaxAttempts (maxAttempts );
358
+ }
358
359
return simple ;
359
360
}
360
361
Map <Class <? extends Throwable >, Boolean > policyMap = new HashMap <>();
@@ -370,63 +371,126 @@ private RetryPolicy getRetryPolicy(Annotation retryable) {
370
371
.withBeanFactory (this .beanFactory );
371
372
}
372
373
else {
373
- return new SimpleRetryPolicy (maxAttempts , policyMap , true , retryNotExcluded );
374
+ SimpleRetryPolicy policy = new SimpleRetryPolicy (maxAttempts , policyMap , true , retryNotExcluded );
375
+ if (Evaluation .RUNTIME .equals (attrs .get ("expressionEvaluation" ))) {
376
+ policy .setMaxAttempts (() -> evaluate (expression , Integer .class ));
377
+ }
378
+ return policy ;
374
379
}
375
380
}
376
381
377
382
private BackOffPolicy getBackoffPolicy (Backoff backoff ) {
378
383
Map <String , Object > attrs = AnnotationUtils .getAnnotationAttributes (backoff );
379
384
long min = backoff .delay () == 0 ? backoff .value () : backoff .delay ();
385
+ boolean evalInit = Evaluation .INITIALIZATION .equals (attrs .get ("expressionEvaluation" ));
380
386
String delayExpression = (String ) attrs .get ("delayExpression" );
387
+ Expression parsedMinExp = null ;
381
388
if (StringUtils .hasText (delayExpression )) {
382
- if (ExpressionRetryPolicy .isTemplate (delayExpression )) {
383
- min = PARSER .parseExpression (resolve (delayExpression ), PARSER_CONTEXT ).getValue (this .evaluationContext ,
384
- Long .class );
385
- }
386
- else {
387
- min = PARSER .parseExpression (resolve (delayExpression )).getValue (this .evaluationContext , Long .class );
389
+ parsedMinExp = parse (delayExpression );
390
+ if (evalInit ) {
391
+ min = parsedMinExp .getValue (this .evaluationContext , Long .class );
388
392
}
389
393
}
390
394
long max = backoff .maxDelay ();
391
395
String maxDelayExpression = (String ) attrs .get ("maxDelayExpression" );
396
+ Expression parsedMaxExp = null ;
392
397
if (StringUtils .hasText (maxDelayExpression )) {
393
- if (ExpressionRetryPolicy .isTemplate (maxDelayExpression )) {
394
- max = PARSER .parseExpression (resolve (maxDelayExpression ), PARSER_CONTEXT )
395
- .getValue (this .evaluationContext , Long .class );
396
- }
397
- else {
398
- max = PARSER .parseExpression (resolve (maxDelayExpression )).getValue (this .evaluationContext , Long .class );
398
+ parsedMaxExp = parse (maxDelayExpression );
399
+ if (evalInit ) {
400
+ max = parsedMaxExp .getValue (this .evaluationContext , Long .class );
399
401
}
400
402
}
401
403
double multiplier = backoff .multiplier ();
402
404
String multiplierExpression = (String ) attrs .get ("multiplierExpression" );
405
+ Expression parsedMultExp = null ;
403
406
if (StringUtils .hasText (multiplierExpression )) {
404
- if (ExpressionRetryPolicy .isTemplate (multiplierExpression )) {
405
- multiplier = PARSER .parseExpression (resolve (multiplierExpression ), PARSER_CONTEXT )
406
- .getValue (this .evaluationContext , Double .class );
407
- }
408
- else {
409
- multiplier = PARSER .parseExpression (resolve (multiplierExpression )).getValue (this .evaluationContext ,
410
- Double .class );
407
+ parsedMultExp = parse (multiplierExpression );
408
+ if (evalInit ) {
409
+ multiplier = parsedMultExp .getValue (this .evaluationContext , Double .class );
411
410
}
412
411
}
413
412
boolean isRandom = false ;
413
+ String randomExpression = (String ) attrs .get ("randomExpression" );
414
+ Expression parsedRandomExp = null ;
414
415
if (multiplier > 0 ) {
415
416
isRandom = backoff .random ();
416
- String randomExpression = (String ) attrs .get ("randomExpression" );
417
417
if (StringUtils .hasText (randomExpression )) {
418
- if (ExpressionRetryPolicy .isTemplate (randomExpression )) {
419
- isRandom = PARSER .parseExpression (resolve (randomExpression ), PARSER_CONTEXT )
420
- .getValue (this .evaluationContext , Boolean .class );
421
- }
422
- else {
423
- isRandom = PARSER .parseExpression (resolve (randomExpression )).getValue (this .evaluationContext ,
424
- Boolean .class );
418
+ parsedRandomExp = parse (randomExpression );
419
+ if (evalInit ) {
420
+ isRandom = parsedRandomExp .getValue (this .evaluationContext , Boolean .class );
425
421
}
426
422
}
427
423
}
428
- return BackOffPolicyBuilder .newBuilder ().delay (min ).maxDelay (max ).multiplier (multiplier ).random (isRandom )
429
- .sleeper (this .sleeper ).build ();
424
+ if (!evalInit ) {
425
+ return buildWithRuntimeExpressions (min , parsedMinExp , max , parsedMaxExp , multiplier , parsedMultExp ,
426
+ isRandom , parsedRandomExp );
427
+ }
428
+ else {
429
+ return BackOffPolicyBuilder .newBuilder ().delay (min ).maxDelay (max ).multiplier (multiplier ).random (isRandom )
430
+ .sleeper (this .sleeper ).build ();
431
+ }
432
+ }
433
+
434
+ private BackOffPolicy buildWithRuntimeExpressions (long min , Expression parsedMinExp , long max ,
435
+ Expression parsedMaxExp , double multiplier , Expression parsedMultExp , boolean isRandom ,
436
+ Expression parsedRandomExp ) {
437
+
438
+ BackOffPolicyBuilder builder = BackOffPolicyBuilder .newBuilder ();
439
+ if (parsedMinExp != null ) {
440
+ Expression expression = parsedMinExp ;
441
+ builder .delaySupplier (() -> evaluate (expression , Long .class ));
442
+ }
443
+ else {
444
+ builder .delay (min );
445
+ }
446
+ if (parsedMaxExp != null ) {
447
+ Expression expression = parsedMaxExp ;
448
+ builder .maxDelaySupplier (() -> evaluate (expression , Long .class ));
449
+ }
450
+ else {
451
+ builder .maxDelay (max );
452
+ }
453
+ if (parsedMultExp != null ) {
454
+ Expression expression = parsedMultExp ;
455
+ builder .multiplierSupplier (() -> evaluate (expression , Double .class ));
456
+ }
457
+ else {
458
+ builder .multiplier (multiplier );
459
+ }
460
+ if (parsedRandomExp != null ) {
461
+ Expression expression = parsedRandomExp ;
462
+ builder .randomSupplier (() -> evaluate (expression , Boolean .class ));
463
+ }
464
+ else {
465
+ builder .random (isRandom );
466
+ }
467
+ return builder .build ();
468
+ }
469
+
470
+ private Expression parse (String expression ) {
471
+ if (isTemplate (expression )) {
472
+ return PARSER .parseExpression (resolve (expression ), PARSER_CONTEXT );
473
+ }
474
+ else {
475
+ return PARSER .parseExpression (resolve (expression ));
476
+ }
477
+ }
478
+
479
+ private boolean isTemplate (String expression ) {
480
+ return expression .contains (PARSER_CONTEXT .getExpressionPrefix ())
481
+ && expression .contains (PARSER_CONTEXT .getExpressionSuffix ());
482
+ }
483
+
484
+ private <T > T evaluate (Expression expression , Class <T > type ) {
485
+ RetryContext context = RetrySynchronizationManager .getContext ();
486
+ Args args = null ;
487
+ if (context != null ) {
488
+ args = (Args ) context .getAttribute ("ARGS" );
489
+ }
490
+ if (args == null ) {
491
+ args = Args .NO_ARGS ;
492
+ }
493
+ return expression .getValue (this .evaluationContext , args , type );
430
494
}
431
495
432
496
/**
0 commit comments