1
1
/*
2
- * Copyright 2002-2018 the original author or authors.
2
+ * Copyright 2002-2020 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package org .springframework .expression .spel .standard ;
18
18
19
+ import java .util .concurrent .atomic .AtomicInteger ;
20
+
19
21
import org .springframework .core .convert .TypeDescriptor ;
20
22
import org .springframework .expression .EvaluationContext ;
21
23
import org .springframework .expression .EvaluationException ;
@@ -62,15 +64,15 @@ public class SpelExpression implements Expression {
62
64
private EvaluationContext evaluationContext ;
63
65
64
66
// Holds the compiled form of the expression (if it has been compiled)
65
- private CompiledExpression compiledAst ;
67
+ private volatile CompiledExpression compiledAst ;
66
68
67
69
// Count of many times as the expression been interpreted - can trigger compilation
68
70
// when certain limit reached
69
- private volatile int interpretedCount = 0 ;
71
+ private final AtomicInteger interpretedCount = new AtomicInteger ( 0 ) ;
70
72
71
73
// The number of times compilation was attempted and failed - enables us to eventually
72
74
// give up trying to compile it when it just doesn't seem to be possible.
73
- private volatile int failedAttempts = 0 ;
75
+ private final AtomicInteger failedAttempts = new AtomicInteger ( 0 ) ;
74
76
75
77
76
78
/**
@@ -112,16 +114,17 @@ public String getExpressionString() {
112
114
113
115
@ Override
114
116
public Object getValue () throws EvaluationException {
115
- if (this .compiledAst != null ) {
117
+ CompiledExpression compiledAst = this .compiledAst ;
118
+ if (compiledAst != null ) {
116
119
try {
117
120
EvaluationContext context = getEvaluationContext ();
118
- return this . compiledAst .getValue (context .getRootObject ().getValue (), context );
121
+ return compiledAst .getValue (context .getRootObject ().getValue (), context );
119
122
}
120
123
catch (Throwable ex ) {
121
124
// If running in mixed mode, revert to interpreted
122
125
if (this .configuration .getCompilerMode () == SpelCompilerMode .MIXED ) {
123
- this .interpretedCount = 0 ;
124
126
this .compiledAst = null ;
127
+ this .interpretedCount .set (0 );
125
128
}
126
129
else {
127
130
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -139,10 +142,11 @@ public Object getValue() throws EvaluationException {
139
142
@ SuppressWarnings ("unchecked" )
140
143
@ Override
141
144
public <T > T getValue (Class <T > expectedResultType ) throws EvaluationException {
142
- if (this .compiledAst != null ) {
145
+ CompiledExpression compiledAst = this .compiledAst ;
146
+ if (compiledAst != null ) {
143
147
try {
144
148
EvaluationContext context = getEvaluationContext ();
145
- Object result = this . compiledAst .getValue (context .getRootObject ().getValue (), context );
149
+ Object result = compiledAst .getValue (context .getRootObject ().getValue (), context );
146
150
if (expectedResultType == null ) {
147
151
return (T ) result ;
148
152
}
@@ -154,8 +158,8 @@ public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
154
158
catch (Throwable ex ) {
155
159
// If running in mixed mode, revert to interpreted
156
160
if (this .configuration .getCompilerMode () == SpelCompilerMode .MIXED ) {
157
- this .interpretedCount = 0 ;
158
161
this .compiledAst = null ;
162
+ this .interpretedCount .set (0 );
159
163
}
160
164
else {
161
165
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -173,15 +177,16 @@ public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
173
177
174
178
@ Override
175
179
public Object getValue (Object rootObject ) throws EvaluationException {
176
- if (this .compiledAst != null ) {
180
+ CompiledExpression compiledAst = this .compiledAst ;
181
+ if (compiledAst != null ) {
177
182
try {
178
- return this . compiledAst .getValue (rootObject , getEvaluationContext ());
183
+ return compiledAst .getValue (rootObject , getEvaluationContext ());
179
184
}
180
185
catch (Throwable ex ) {
181
186
// If running in mixed mode, revert to interpreted
182
187
if (this .configuration .getCompilerMode () == SpelCompilerMode .MIXED ) {
183
- this .interpretedCount = 0 ;
184
188
this .compiledAst = null ;
189
+ this .interpretedCount .set (0 );
185
190
}
186
191
else {
187
192
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -200,9 +205,10 @@ public Object getValue(Object rootObject) throws EvaluationException {
200
205
@ SuppressWarnings ("unchecked" )
201
206
@ Override
202
207
public <T > T getValue (Object rootObject , Class <T > expectedResultType ) throws EvaluationException {
203
- if (this .compiledAst != null ) {
208
+ CompiledExpression compiledAst = this .compiledAst ;
209
+ if (compiledAst != null ) {
204
210
try {
205
- Object result = this . compiledAst .getValue (rootObject , getEvaluationContext ());
211
+ Object result = compiledAst .getValue (rootObject , getEvaluationContext ());
206
212
if (expectedResultType == null ) {
207
213
return (T )result ;
208
214
}
@@ -214,8 +220,8 @@ public <T> T getValue(Object rootObject, Class<T> expectedResultType) throws Eva
214
220
catch (Throwable ex ) {
215
221
// If running in mixed mode, revert to interpreted
216
222
if (this .configuration .getCompilerMode () == SpelCompilerMode .MIXED ) {
217
- this .interpretedCount = 0 ;
218
223
this .compiledAst = null ;
224
+ this .interpretedCount .set (0 );
219
225
}
220
226
else {
221
227
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -236,15 +242,16 @@ public <T> T getValue(Object rootObject, Class<T> expectedResultType) throws Eva
236
242
public Object getValue (EvaluationContext context ) throws EvaluationException {
237
243
Assert .notNull (context , "EvaluationContext is required" );
238
244
239
- if (this .compiledAst != null ) {
245
+ CompiledExpression compiledAst = this .compiledAst ;
246
+ if (compiledAst != null ) {
240
247
try {
241
- return this . compiledAst .getValue (context .getRootObject ().getValue (), context );
248
+ return compiledAst .getValue (context .getRootObject ().getValue (), context );
242
249
}
243
250
catch (Throwable ex ) {
244
251
// If running in mixed mode, revert to interpreted
245
252
if (this .configuration .getCompilerMode () == SpelCompilerMode .MIXED ) {
246
- this .interpretedCount = 0 ;
247
253
this .compiledAst = null ;
254
+ this .interpretedCount .set (0 );
248
255
}
249
256
else {
250
257
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -264,9 +271,10 @@ public Object getValue(EvaluationContext context) throws EvaluationException {
264
271
public <T > T getValue (EvaluationContext context , Class <T > expectedResultType ) throws EvaluationException {
265
272
Assert .notNull (context , "EvaluationContext is required" );
266
273
267
- if (this .compiledAst != null ) {
274
+ CompiledExpression compiledAst = this .compiledAst ;
275
+ if (compiledAst != null ) {
268
276
try {
269
- Object result = this . compiledAst .getValue (context .getRootObject ().getValue (), context );
277
+ Object result = compiledAst .getValue (context .getRootObject ().getValue (), context );
270
278
if (expectedResultType != null ) {
271
279
return ExpressionUtils .convertTypedValue (context , new TypedValue (result ), expectedResultType );
272
280
}
@@ -277,8 +285,8 @@ public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) th
277
285
catch (Throwable ex ) {
278
286
// If running in mixed mode, revert to interpreted
279
287
if (this .configuration .getCompilerMode () == SpelCompilerMode .MIXED ) {
280
- this .interpretedCount = 0 ;
281
288
this .compiledAst = null ;
289
+ this .interpretedCount .set (0 );
282
290
}
283
291
else {
284
292
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -297,15 +305,16 @@ public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) th
297
305
public Object getValue (EvaluationContext context , Object rootObject ) throws EvaluationException {
298
306
Assert .notNull (context , "EvaluationContext is required" );
299
307
300
- if (this .compiledAst != null ) {
308
+ CompiledExpression compiledAst = this .compiledAst ;
309
+ if (compiledAst != null ) {
301
310
try {
302
- return this . compiledAst .getValue (rootObject , context );
311
+ return compiledAst .getValue (rootObject , context );
303
312
}
304
313
catch (Throwable ex ) {
305
314
// If running in mixed mode, revert to interpreted
306
315
if (this .configuration .getCompilerMode () == SpelCompilerMode .MIXED ) {
307
- this .interpretedCount = 0 ;
308
316
this .compiledAst = null ;
317
+ this .interpretedCount .set (0 );
309
318
}
310
319
else {
311
320
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -327,9 +336,10 @@ public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> exp
327
336
328
337
Assert .notNull (context , "EvaluationContext is required" );
329
338
330
- if (this .compiledAst != null ) {
339
+ CompiledExpression compiledAst = this .compiledAst ;
340
+ if (compiledAst != null ) {
331
341
try {
332
- Object result = this . compiledAst .getValue (rootObject , context );
342
+ Object result = compiledAst .getValue (rootObject , context );
333
343
if (expectedResultType != null ) {
334
344
return ExpressionUtils .convertTypedValue (context , new TypedValue (result ), expectedResultType );
335
345
}
@@ -340,8 +350,8 @@ public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> exp
340
350
catch (Throwable ex ) {
341
351
// If running in mixed mode, revert to interpreted
342
352
if (this .configuration .getCompilerMode () == SpelCompilerMode .MIXED ) {
343
- this .interpretedCount = 0 ;
344
353
this .compiledAst = null ;
354
+ this .interpretedCount .set (0 );
345
355
}
346
356
else {
347
357
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -454,48 +464,58 @@ public void setValue(EvaluationContext context, Object rootObject, Object value)
454
464
* @param expressionState the expression state used to determine compilation mode
455
465
*/
456
466
private void checkCompile (ExpressionState expressionState ) {
457
- this .interpretedCount ++ ;
467
+ this .interpretedCount . incrementAndGet () ;
458
468
SpelCompilerMode compilerMode = expressionState .getConfiguration ().getCompilerMode ();
459
469
if (compilerMode != SpelCompilerMode .OFF ) {
460
470
if (compilerMode == SpelCompilerMode .IMMEDIATE ) {
461
- if (this .interpretedCount > 1 ) {
471
+ if (this .interpretedCount . get () > 1 ) {
462
472
compileExpression ();
463
473
}
464
474
}
465
475
else {
466
476
// compilerMode = SpelCompilerMode.MIXED
467
- if (this .interpretedCount > INTERPRETED_COUNT_THRESHOLD ) {
477
+ if (this .interpretedCount . get () > INTERPRETED_COUNT_THRESHOLD ) {
468
478
compileExpression ();
469
479
}
470
480
}
471
481
}
472
482
}
473
483
474
-
475
484
/**
476
- * Perform expression compilation. This will only succeed once exit descriptors for all nodes have
477
- * been determined. If the compilation fails and has failed more than 100 times the expression is
478
- * no longer considered suitable for compilation.
485
+ * Perform expression compilation. This will only succeed once exit descriptors for
486
+ * all nodes have been determined. If the compilation fails and has failed more than
487
+ * 100 times the expression is no longer considered suitable for compilation.
488
+ * @return whether this expression has been successfully compiled
479
489
*/
480
490
public boolean compileExpression () {
481
- if (this .failedAttempts > FAILED_ATTEMPTS_THRESHOLD ) {
491
+ CompiledExpression compiledAst = this .compiledAst ;
492
+ if (compiledAst != null ) {
493
+ // Previously compiled
494
+ return true ;
495
+ }
496
+ if (this .failedAttempts .get () > FAILED_ATTEMPTS_THRESHOLD ) {
482
497
// Don't try again
483
498
return false ;
484
499
}
485
- if (this .compiledAst == null ) {
486
- synchronized (this .expression ) {
487
- // Possibly compiled by another thread before this thread got into the sync block
488
- if (this .compiledAst != null ) {
489
- return true ;
490
- }
491
- SpelCompiler compiler = SpelCompiler .getCompiler (this .configuration .getCompilerClassLoader ());
492
- this .compiledAst = compiler .compile (this .ast );
493
- if (this .compiledAst == null ) {
494
- this .failedAttempts ++;
495
- }
500
+
501
+ synchronized (this ) {
502
+ if (this .compiledAst != null ) {
503
+ // Compiled by another thread before this thread got into the sync block
504
+ return true ;
505
+ }
506
+ SpelCompiler compiler = SpelCompiler .getCompiler (this .configuration .getCompilerClassLoader ());
507
+ compiledAst = compiler .compile (this .ast );
508
+ if (compiledAst != null ) {
509
+ // Successfully compiled
510
+ this .compiledAst = compiledAst ;
511
+ return true ;
512
+ }
513
+ else {
514
+ // Failed to compile
515
+ this .failedAttempts .incrementAndGet ();
516
+ return false ;
496
517
}
497
518
}
498
- return (this .compiledAst != null );
499
519
}
500
520
501
521
/**
@@ -505,8 +525,8 @@ public boolean compileExpression() {
505
525
*/
506
526
public void revertToInterpreted () {
507
527
this .compiledAst = null ;
508
- this .interpretedCount = 0 ;
509
- this .failedAttempts = 0 ;
528
+ this .interpretedCount . set ( 0 ) ;
529
+ this .failedAttempts . set ( 0 ) ;
510
530
}
511
531
512
532
/**
0 commit comments