#java #html #thymeleaf #ognl
#java #HTML #thymeleaf #ognl
Вопрос:
У меня есть шаблон Thymeleaf, без использования каких-либо Spring или SpEL — только стандартный диалект Thymeleaf.
Соответствующая часть шаблона:
<div>
<span>Inactive: </span>
<span th:text="${total - active}"></span>
</div>
Тест 1
Если моя модель заполнена следующим образом:
model.put("total", 10);
model.put("active", 7);
затем я получаю ожидаемый результат:
<div>
<span>Inactive: </span>
<span>3</span>
</div>
Тест 2
Но если одно (или оба) значения модели равно null:
model.put("total", null);
model.put("active", 7);
затем я получаю неожиданный результат. С приведенными выше данными я получаю:
<div>
<span>Inactive: </span>
<span>-7.0</span>
</div>
Другими словами, null
вычисляется как значение с плавающей запятой 0.0
, и поэтому результат вычисляется как -7.0
.
Я бы ожидал, что он выдаст ошибку времени выполнения, вызванную попыткой выполнить арифметику для нулевого значения.
Тест 3
Если я немного изменю шаблон и использую те же данные, что и в тесте 2:
<div>
<span>Inactive: </span>
<span th:text="${total} - ${active}"></span>
</div>
Я получаю следующую ошибку времени выполнения — это то, что я ожидал для теста 2:
org.thymeleaf.exceptions.Исключение TemplateProcessingException: невозможно выполнить вычитание: операнды «null» и «7»
Почему тест 2 генерирует действительный HTML-код с неожиданным результатом вычисления вместо того, чтобы выдавать ошибку?
(Я понимаю, что мне нужно заранее обработать эти null
значения, чтобы избежать проблем, но это молчаливое отсутствие сбоев является проблемой.)
Ответ №1:
Быстрый ответ
Вы должны обрабатывать null
значения явно заранее, как указано в вопросе.
Вы также должны знать о несогласованном поведении между оператором минус Thymeleaf и оператором минус OGNL.
Рекомендация: используйте оператор Thymeleaf минус.
Далее следует более подробное объяснение…
Thymeleaf и OGNL
При использовании стандартного диалекта Thymeleaf выражения внутри открытия ${
и закрытия }
обрабатываются OGNL. Из официальной документации:
Это выражение переменной, и оно содержит выражение на языке, называемом OGNL (Object-Graph Navigation Language), которое будет выполнено на карте контекстных переменных…
Вы можете прочитать об OGNL и его синтаксисе здесь . Thymeleaf версии 3.0.12 использует ognl-3.1.26.jar
библиотеку.
Итак, когда вы используете ${total - active}
, все это выражение, включая вычитание, обрабатывается OGNL.
Однако, когда вы используете ${total} - ${active}
, это фактически два отдельных выражения OGNL со знаком минус между ними.
В первом случае ответственность за выполнение вычитания лежит на OGNL. Но во втором случае вычитание выполняется Thymeleaf после того, как он делегировал оценку двух ${...}
выражений в OGNL.
Thymeleaf обычно показывает то же поведение, что и OGNL для большинства своих операторов. Но в этом конкретном случае оператор Thymeleaf minus при обработке значений ведет себя иначе, чем оператор OGNL minus null
.
Я бы сказал, что поведение Thymeleaf является менее неожиданным подходом.
Spring и SpEL
Если вы используете Spring с Thymeleaf, то все выражения внутри ${
и }
делегируются SpEL (языку выражений Spring), а OGNL вообще не используется.
В этом случае выражение ${total - active}
больше не будет «успешным» во время выполнения. Вместо этого он выдаст исключение SpEL:
org.springframework.expression.spel.Исключение SpelEvaluationException: EL1030E: оператор ‘SUBTRACT’ не поддерживается между объектами типа ‘null’ и ‘java.lang.Целое число ‘
Итак, SpEL и стандартный диалект Thymeleaf совместимы друг с другом, а OGNL находится в меньшинстве.
Действительно ли OGNL оценивает null как ноль здесь?
Да — и мы можем продемонстрировать это, используя OGNL в простой программе Java:
Зависимость:
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.3.0</version>
<!-- same behavior:
<version>3.2.21</version>
-->
</dependency>
Демонстрация Java:
import ognl.Ognl;
import ognl.OgnlException;
public class NullDemo {
public void run() throws OgnlException {
// this uses a null context (2nd parameter), because we have
// a simple hard-coded subtraction expression:
Object result = Ognl.getValue("null - 7", null);
System.out.println(result);
}
}
Вывод такой -7.0
же, как в тесте № 2 в вопросе.
Подтверждения
Я чрезвычайно благодарен команде Thymeleaf за то, что они указали мне правильное направление для решения этой проблемы:
Неожиданный результат вычитания с использованием нулей
Postscript
Выражение OGNL:
"1 / null"
вычисляется в Java Double.POSITIVE_INFINITY
по тем же причинам, что и описанное выше поведение — where null
преобразуется в 0.0
.