LoginSignup
2
2

More than 3 years have passed since last update.

[SpringBoot+Thymeleaf] 負荷をかけた時だけコンパイルに失敗してハマった話

Posted at

環境

  • Spring Boot 2.1.6.RELEASE
  • Thymeleaf 3.0.4.RELEASE

何が起きたのか?

ローカルで開発をしている時、または開発環境などの少人数で画面を操作している時には問題なく動作していた画面が突然コンパイルに失敗したというエラーが出力されるようになった。

画面のリロードを一定回数行った後、再現することが分かったが謎すぎる。
しかも、リロードする度にエラーが発生する行が変わる。
暫定的な解消方法としては再起動すれば直る。(マジで気味が悪い・・・)

これが俗に言う動いていたものが動かなくなったというものか・・・と半日もハマってた。

エラー内容

Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "xxxList[j].element" (template: "test/xxx.html" - line 111, col 51)
    at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:290)
    at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166)
    at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66)
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138)
    at org.thymeleaf.standard.expression.LinkExpression.resolveParameters(LinkExpression.java:337)
    at org.thymeleaf.standard.expression.LinkExpression.executeLinkExpression(LinkExpression.java:283)
    at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:85)
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138)
    at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:144)
    at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74)
    at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95)
    at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633)
    at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314)
    at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205)
    at org.thymeleaf.engine.Model.process(Model.java:282)
    at org.thymeleaf.engine.Model.process(Model.java:290)
    at org.thymeleaf.engine.IteratedGatheringModelProcessable.processIterationModel(IteratedGatheringModelProcessable.java:367)
    at org.thymeleaf.engine.IteratedGatheringModelProcessable.process(IteratedGatheringModelProcessable.java:221)
    at org.thymeleaf.engine.ProcessorTemplateHandler.handleCloseElement(ProcessorTemplateHandler.java:1640)
    at org.thymeleaf.engine.CloseElementTag.beHandled(CloseElementTag.java:139)
    at org.thymeleaf.engine.Model.process(Model.java:282)
    at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1587)
    at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205)
    at org.thymeleaf.engine.Model.process(Model.java:282)
    at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1587)
    at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205)
    at org.thymeleaf.engine.Model.process(Model.java:282)
    at org.thymeleaf.engine.Model.process(Model.java:290)
    at org.thymeleaf.engine.GatheringModelProcessable.process(GatheringModelProcessable.java:78)
    at org.thymeleaf.engine.ProcessorTemplateHandler.handleCloseElement(ProcessorTemplateHandler.java:1640)
    at org.thymeleaf.engine.CloseElementTag.beHandled(CloseElementTag.java:139)
    at org.thymeleaf.engine.TemplateModel.process(TemplateModel.java:136)
    at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:592)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072)
    at org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:362)
    at org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:189)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1371)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1117)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1056)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
    ... 97 common frames omitted
Caused by: java.lang.IllegalStateException: Failed to instantiate CompiledExpression
    at org.springframework.expression.spel.standard.SpelCompiler.compile(SpelCompiler.java:111)
    at org.springframework.expression.spel.standard.SpelExpression.compileExpression(SpelExpression.java:511)
    at org.springframework.expression.spel.standard.SpelExpression.checkCompile(SpelExpression.java:487)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:329)
    at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:263)
    ... 140 common frames omitted
Caused by: java.lang.VerifyError: (class: spel/Ex170, method: getValue signature: (Ljava/lang/Object;Lorg/springframework/expression/EvaluationContext;)Ljava/lang/Object;) Expecting to find integer on stack
    at java.base/java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3138)
    at java.base/java.lang.Class.getConstructor0(Class.java:3343)
    at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2554)
    at org.springframework.util.ReflectionUtils.accessibleConstructor(ReflectionUtils.java:190)
    at org.springframework.expression.spel.standard.SpelCompiler.compile(SpelCompiler.java:108)
    ... 144 common frames omitted

結論

結論から言うとThymeleafではリストまたは配列の添字処理を行う時にプリプロセッシングと呼ばれる処理を行わないといけなかった。

NG
<div th:each="i : ${#numbers.sequence(0, xxxList.size()-1)}">
  <div th:text="${xxxList[i].element}"></div>
</div>
OK
<div th:each="i : ${#numbers.sequence(0, xxxList.size()-1)}">
  <div th:text="${xxxList[__${i}__].element}"></div>
</div>

${xxxList[${i}]}の式は本来xxxList[0], xxxList[1], xxxList[2]と評価されないといけないが、xxxList[i], xxxList[i], xxxList[i]のようにiが変数(数字)と評価される前に文字列として評価されてしまっていた。

これを解消するためには評価順番を指定する必要があり${xxxList[__${i}__]}のように変数の左右に"__"を使うことで先に評価する式を明示的に指定することができる。

今回の場合は負荷をかけることで評価する順番が変わってしまったため、リロードして負荷をあげた瞬間にコンパイルに失敗した、という訳だ。

[参考]
https://macchinetta.github.io/server-guideline-thymeleaf/current/ja/ImplementationAtEachLayer/ApplicationLayer.html#view-thymeleaf-preprocessing-label

最後に

Thmeleafでは添字する時にはプリプロセッシングを行わないといけない。という決まりごとがあるので他の人が同じような事象に陥らない。

故に無視したらどのような挙動になるのかの記事が全くなく、かなりの時間を費やしてしまったがいい勉強になった。

この事象に悩んでいる人の役に立っていれば幸いです!

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2