Skip to content

SpEL fails to recover from error during MIXED mode compilation #28043

Closed
@happier233

Description

@happier233

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

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions