«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'}}">
 

Совет: распространенная ошибка в учебниках при определении динамических карт использует двойные кавычки (-) вместо одинарных кавычек для ключа карты — “ это приведет к ошибке компиляции 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 \u003cc:set/\u003e, чтобы объявить свою структуру данных как переменную уровня страницы и манипулировать ею на странице 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.baeldung.el.controllers.ELSampleBean");
         }
     });
 }

Затем мы определяем константное поле String (или Enum, если вы выберете) в желаемом class:

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.