SpEL fails to recover from error during MIXED mode compilation (original) (raw)
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