1. Обзор
В этой статье мы рассмотрим последние функции, улучшения и проблемы совместимости языка выражений версии 3.0 (EL 3.0).
Это последняя версия на момент написания этой статьи, и она поставляется с более поздними серверами приложений JavaEE (JBoss EAP 7 и Glassfish 4 — хорошие примеры, в которых реализована ее поддержка).
Статья сосредоточена только на разработках в EL 3.0 — чтобы узнать больше о языке выражений в целом, сначала прочитайте статью EL версии 2.2 .
2. Предпосылки
Примеры, показанные в статье, также были протестированы на Tomcat 8. Чтобы использовать EL3.0, вы должны добавить следующую зависимость:
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
Вы всегда можете проверить репозиторий Maven на наличие последней зависимости, перейдя по этой ссылке .
3. Лямбда-выражения
Последняя итерация EL обеспечивает очень надежную поддержку лямбда-выражений. Лямбда-выражения появились в Java SE 8, но их поддержка в EL появилась в Java EE 7.
Реализация здесь полнофункциональная, что обеспечивает большую гибкость (и некоторый подразумеваемый риск) в использовании и оценке EL.
3.1. Выражения значений Lambda EL
Базовое использование этой функции позволяет нам указать лямбда-выражение в качестве типа значения в выражении значения EL:
<h:outputText id="valueOutput"
value="#{(x->x*x*x);(ELBean.pageCounter)}"/>
Исходя из этого, можно назвать лямбда-функцию в EL для повторного использования в составных операторах, точно так же, как в лямбда-выражении в Java SE. Составные лямбда-выражения могут быть разделены точкой с запятой ( ;
):
<h:outputText id="valueOutput"
value="#{cube=(x->x*x*x);cube(ELBean.pageCounter)}"/>
Этот фрагмент присваивает функцию идентификатору куба
, который сразу же становится доступным для повторного использования.
3.2. Передача лямбда-выражений в вспомогательный компонент
Давайте пойдем немного дальше: мы можем получить большую гибкость, инкапсулируя логику в выражение EL (в виде лямбда) и передавая его вспомогательному компоненту JSF:
<h:outputText id="valueOutput"
value="#{ELBean.multiplyValue(x->x*x*x)}"/>
Теперь это позволяет нам обрабатывать лямбда-выражение целиком как экземпляр javax.el.LambdaExpression
:
public String multiplyValue(LambdaExpression expr){
return (String) expr.invoke(
FacesContext.getCurrentInstance().getELContext(), pageCounter);
}
Это привлекательная функция, которая позволяет:
- Чистый способ упаковки логики, обеспечивающий очень гибкую парадигму функционального программирования. Вышеприведенная логика поддерживающего компонента может быть условной на основе значений, полученных из разных источников.
- Простой способ внедрить поддержку лямбда-выражений в базы кода до JDK 8, которые могут быть не готовы к обновлению.
- Мощный инструмент для использования нового API Streams/Collections.
4. Усовершенствования API коллекций
Поддержка API коллекций в более ранних версиях EL несколько отсутствовала. В EL 3.0 представлены значительные улучшения API для поддержки коллекций Java, и, как и в случае с лямбда-выражениями, EL 3.0 обеспечивает поддержку потоковой передачи JDK 8 в Java EE 7.
4.1. Определение динамических коллекций
Новое в 3.0, теперь мы можем динамически определять специальные структуры данных в EL:
- Списки:
<h:dataTable var="listItem" value="#{['1','2','3']}">
<h:column id="nameCol">
<h:outputText id="name" value="#{listItem}"/>
</h:column>
</h:dataTable>
- Наборы:
<h:dataTable var="setResult" value="#{{'1','2','3'}}">
....
</h:dataTable>
Примечание.
Как и в обычных наборах Java,
порядок элементов в списке непредсказуем.
- Карты:
<h:dataTable var="mapResult"
value="#{{'one':'1','two':'2','three':'3'}}">
Совет
: распространенная ошибка в учебниках при определении динамических карт заключается в использовании двойных кавычек (") вместо одинарных кавычек для ключа Map - это приведет к ошибке компиляции EL.
4.2. Расширенные операции сбора
В версии EL3.0 реализована поддержка расширенной семантики запросов, которая сочетает в себе мощь лямбда-выражений, новый потоковый API и SQL-подобные операции, такие как соединения и группировка. Мы не будем рассматривать их в этой статье, так как это дополнительные темы. Давайте посмотрим на образец, чтобы продемонстрировать его мощность:
<h:dataTable var="streamResult"
value="#{['1','2','3'].stream().filter(x-> x>1).toList()}">
<h:column id="nameCol">
<h:outputText id="name" value="#{streamResult}"/>
</h:column>
</h:dataTable>
В приведенной выше таблице будет отфильтрован резервный список с использованием переданного лямбда-выражения.
<h:outputLabel id="avgLabel" for="avg"
value="Average of integer list value"/>
<h:outputText id="avg"
value="#{['1','2','3'].stream().average().get()}"/>
Выходной текст avg
будет вычислять среднее число чисел в списке. Обе эти операции защищены от нулевых значений благодаря новому дополнительному
API (еще одно улучшение по сравнению с предыдущими версиями).
Помните, что для поддержки этого не требуется JDK 8, только JavaEE 7/EL3.0. Это означает, что вы можете выполнять большинство операций JDK 8 Stream
в EL, но не в Java-коде резервного компонента.
Совет.
Вы можете использовать тег JSTL <c:set/>
, чтобы объявить свою структуру данных как переменную уровня страницы и манипулировать ею на странице JSF:
<c:set var='pageLevelNumberList' value="#{[1,2,3]}"/>
Теперь вы можете ссылаться на «#{pageLevelNumberList}»
на всей странице, как будто это настоящий компонент или компонент JSF. Это позволяет значительно повторно использовать всю страницу.
<h:outputText id="avg"
value="#{pageLevelNumberList.stream().average().get()}"/>
5. Статические поля и методы
В предыдущих версиях EL не было поддержки статического поля, метода или доступа к Enum. Времена изменились.
Во-первых, мы должны вручную импортировать класс, содержащий константы, в контекст EL. В идеале это нужно сделать как можно раньше. Здесь мы делаем это в инициализаторе @PostConstruct
управляемого компонента JSF ( подходящим кандидатом также является ServletContextListener ):
@PostConstruct
public void init() {
FacesContext.getCurrentInstance()
.getApplication().addELContextListener(new ELContextListener() {
@Override
public void contextCreated(ELContextEvent evt) {
evt.getELContext().getImportHandler()
.importClass("com.foreach.el.controllers.ELSampleBean");
}
});
}
Затем мы определяем константное поле String (или
Enum
, если хотите) в нужном классе:
public static final String constantField
= "THIS_IS_NOT_CHANGING_ANYTIME_SOON";
После чего теперь мы можем получить доступ к переменной в EL:
<h:outputLabel id="staticLabel"
for="staticFieldOutput" value="Constant field access: "/>
<h:outputText id="staticFieldOutput"
value="#{ELSampleBean.constantField}"/>
Согласно спецификации EL 3.0, любой класс за пределами java.lang.*
необходимо импортировать вручную, как показано. Только после этого константы, определенные в классе, становятся доступными в EL. Импорт в идеале выполняется как часть инициализации среды выполнения JSF.
Здесь необходимо сделать несколько замечаний:
- Синтаксис требует, чтобы поля и методы были
общедоступными, статическими
(иокончательными
в случае методов). - Синтаксис изменился между первоначальным проектом спецификации EL 3.0 и версией выпуска. Итак, в некоторых учебниках вы все еще можете найти что-то похожее на:
T(YourClass).yourStaticVariableOrMethod
На практике это не сработает (решение об изменении дизайна для упрощения синтаксиса было принято в конце цикла реализации)
- Окончательный выпущенный синтаксис по-прежнему содержал ошибку — важно использовать последние версии.
6. Заключение
Мы рассмотрели некоторые основные моменты последней реализации EL. Были внесены значительные улучшения, чтобы добавить в API новые интересные функции, такие как лямбда-выражение и гибкость потоков.
С той гибкостью, которую мы теперь имеем в EL, важно помнить об одной из целей разработки среды JSF: четкое разделение задач с использованием шаблона MVC.
Так что стоит отметить, что последние улучшения API могут открыть нам путь к анти-шаблонам в JSF, потому что EL теперь имеет возможность выполнять реальную бизнес-логику — больше, чем раньше. И поэтому важно помнить об этом во время реальной реализации, чтобы убедиться, что обязанности четко разделены.
И, конечно же, примеры из статей можно найти на GitHub.