Closed
Description
Affects: 5.2.13.RELEASE
I am testing spel performance, when I share a SpelExpression instance between multiple threads.
I tried set SpelCompilerMode to MIXED or IMMEDIATE and put a variable with different type in the context, then I triggered an exception.
If I set SpelCompilerMode to OFF, it works correctly. So I am confused, is spel thread safe?
I didn't find any description of spel's thread safety in the spring project document. I want to ask it's a spel thread safe bug or spel is not thread safe when compile is enable.
Here is my test code:
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.DataBindingMethodResolver;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author happier233
* @version Main.java, v 0.1 2021年08月18日 10:02 上午 happier233
*/
public class Main {
public static String name = "default";
public static Context root = new Context();
public static SpelParserConfiguration spelParserConfiguration = new SpelParserConfiguration(
SpelCompilerMode.MIXED, Main.class.getClassLoader());
public static ExpressionParser parser = new SpelExpressionParser(
spelParserConfiguration);
public static Expression expression = parser
.parseExpression("#bean.name + '234'");
public static void main(String[] args) {
calc(() -> {
for (int i = 0; i < 10; i++) {
calc(() -> run(true));
}
});
new Thread(Main::run).start();
new Thread(Main::run).start();
}
public static void run() {
calc(() -> {
for (int i = 0; i < 1000000; i++) {
run(false);
}
});
}
private static final AtomicInteger cc = new AtomicInteger();
private static void run(boolean flag) {
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding()
.withRootObject(root)
.withMethodResolvers(DataBindingMethodResolver.forInstanceMethodInvocation()).build();
if ((cc.incrementAndGet() & 1) == 0) {
context.setVariable("bean", new Bean("test"));
} else {
context.setVariable("bean", new Bean2(123));
}
Object value = expression.getValue(context);
if (flag) {
System.out.println(value);
}
}
public static void calc(Runnable runnable) {
long start = System.currentTimeMillis();
runnable.run();
long end = System.currentTimeMillis();
System.out.println("time[" + name + "]: " + (end - start));
}
@Data
public static class Context {
private String title = "233";
private AtomicInteger count = new AtomicInteger(0);
}
@Data
@AllArgsConstructor
public static class Bean {
private String name;
}
@Data
@AllArgsConstructor
public static class Bean2 {
private Integer name;
}
}
Here is the excpetion stack:
Exception in thread "Thread-1" java.lang.IllegalStateException: Failed to instantiate CompiledExpression
at org.springframework.expression.spel.standard.SpelCompiler.compile(SpelCompiler.java:113)
at org.springframework.expression.spel.standard.SpelExpression.compileExpression(SpelExpression.java:526)
at org.springframework.expression.spel.standard.SpelExpression.checkCompile(SpelExpression.java:497)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:273)
at org.example.acm.Main.run(Main.java:65)
at org.example.acm.Main.lambda$run$2(Main.java:49)
at org.example.acm.Main.calc(Main.java:73)
at org.example.acm.Main.run(Main.java:47)
at java.lang.Thread.run(Thread.java:750)
Caused by: java.lang.VerifyError: (class: spel/Ex27, method: getValue signature: (Ljava/lang/Object;Lorg/springframework/expression/EvaluationContext;)Ljava/lang/Object;) Incompatible object argument for function call
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at org.springframework.util.ReflectionUtils.accessibleConstructor(ReflectionUtils.java:185)
at org.springframework.expression.spel.standard.SpelCompiler.compile(SpelCompiler.java:110)
... 8 more