Guide to JSF Expression Language 3.0 – JSF表达式语言3.0指南

最后修改: 2016年 8月 15日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll look at the latest features, improvements and compatibility issues of Expression Language, version 3.0 (EL 3.0).

在这篇文章中,我们将看一下表达式语言3.0版(EL 3.0)的最新功能、改进和兼容性问题。

This is the latest version as at the time of this writing and ships with more recent JavaEE application servers (JBoss EAP 7 and Glassfish 4 are good examples that have implemented support for it).

这是本文写作时的最新版本,并与最近的JavaEE应用服务器一起使用(JBoss EAP 7和Glassfish 4是实现了对它的支持的很好例子)。

The article is focused on the developments in EL 3.0 only – to learn more about Expression Language in general, read the EL version 2.2 article first.

这篇文章只关注EL 3.0的发展–要了解更多关于表达式语言的一般情况,请先阅读EL 2.2版文章。

2. Prerequisites

2.先决条件

The examples shown in the article have been tested against Tomcat 8 as well. To use EL3.0, you must add the following dependency:

文章中显示的例子也已经在Tomcat 8上进行了测试。要使用EL3.0,你必须添加以下依赖关系。

<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.0</version>
</dependency>

You can always check the Maven repository for the latest dependency by following this link.

您可以随时按照这个链接查看Maven资源库的最新依赖性。

3. Lambda Expressions

3.Lambda表达式

The latest EL iteration provides very robust support for lambda expressions. Lambda expressions were introduced into Java SE 8, but support for it in EL comes with Java EE 7.

最新的EL迭代为lambda表达式提供了非常强大的支持。兰姆达表达式被引入到Java SE 8中,但在EL中对它的支持来自于Java EE 7。

The implementation here is full-featured, allowing for lots of flexibility (and some implied risk) in EL use and evaluation.

这里的实现是全功能的,允许在EL的使用和评估方面有很多灵活性(和一些隐含的风险)。

3.1. Lambda EL Value Expressions

3.1.Lambda EL值表达式

Basic use of this functionality allows us to specify a lambda expression as the value type in an EL value expression:

对这个功能的基本使用允许我们在EL值表达式中指定一个lambda表达式作为值类型。

<h:outputText id="valueOutput" 
  value="#{(x->x*x*x);(ELBean.pageCounter)}"/>

Extending from that, one can name the lambda function in EL for reuse in compound statements, just like you would in a lambda expression in Java SE. Compound lambda expressions can be separated by a semi-colon ( ;):

由此延伸,人们可以在EL中命名lambda函数,以便在复合语句中重复使用,就像在Java SE中的lambda表达式一样。复合lambda表达式可以用分号(;)分开。

<h:outputText id="valueOutput" 
  value="#{cube=(x->x*x*x);cube(ELBean.pageCounter)}"/>

This snippet assigns the function to the cube identifier, which is then available for reuse immediately.

这个片段将函数分配给cube标识符,然后可立即重复使用。

3.2. Passing Lambda Expressions to the Backing Bean

3.2.将Lambda表达式传递给支持 Bean

Let’s take this a little further: we can get a lot of flexibility by encapsulating logic in an EL expression (as a lambda) and passing it to the JSF backing bean:

让我们再进一步:我们可以通过将逻辑封装在EL表达式中(作为lambda),并将其传递给JSF的支持Bean,从而获得很大的灵活性。

<h:outputText id="valueOutput" 
  value="#{ELBean.multiplyValue(x->x*x*x)}"/>

This now allows us to process the lambda expression whole as an instance of javax.el.LambdaExpression:

现在我们可以把lambda表达式整体作为javax.el.LambdaExpression的一个实例来处理。

public String multiplyValue(LambdaExpression expr){
    return (String) expr.invoke( 
      FacesContext.getCurrentInstance().getELContext(), pageCounter);
}

This is a compelling feature that allows:

这是一个引人注目的功能,允许。

  • A clean way to package logic, providing for a very flexible functional programming paradigm. The backing bean logic above could be conditional based on values pulled in from different sources.
  • A simple way to introduce lambda support in pre-JDK 8 code-bases that might not be ready to upgrade.
  • A powerful tool in using the new Streams/Collections API.

4. Collections API Enhancements

4.集合API的增强

The support for the Collections API in earlier versions of EL was somewhat lacking. EL 3.0 has introduced major API improvements in its support for the Java Collections, and just like the lambda expressions, EL 3.0 provides JDK 8 Streaming support within Java EE 7.

在早期版本的EL中,对集合API的支持有些不足。EL 3.0在对Java集合的支持中引入了主要的API改进,就像lambda表达式一样,EL 3.0在Java EE 7中提供了JDK 8 Streaming支持。

4.1. Dynamic Collections Definition

4.1.动态集合的定义

New in 3.0, we can now dynamically define ad-hoc data structures in EL:

在3.0中的新功能,我们现在可以在EL中动态地定义特设的数据结构。

  • Lists:
   <h:dataTable var="listItem" value="#{['1','2','3']}">
       <h:column id="nameCol">
           <h:outputText id="name" value="#{listItem}"/>
       </h:column>
   </h:dataTable>
  • Sets:
   <h:dataTable var="setResult" value="#{{'1','2','3'}}">
    ....
   </h:dataTable>

Note: As with normal Java Sets, the order of the elements is unpredictable, when listed

注意:和普通的Java Sets一样,元素的顺序是不可预测的,当列出

  • Maps:
   <h:dataTable var="mapResult" 
     value="#{{'one':'1','two':'2','three':'3'}}">
 

Tip: A common mistake in textbooks when defining dynamic maps uses double quotes (“) instead of single quote for the Map key – it’s going to result in an EL compilation error.

Tip:在定义动态地图时,教科书中的一个常见错误是使用双引号(”)而不是Map键的单引号–这将会导致EL编译错误。

4.2. Advanced Collection Operations

4.2.高级收款业务

With EL3.0, there is support for an advanced query semantics that combines the power of lambda expressions, the new streaming API and SQL-like operations like joins and grouping. We won’t cover these in this article as these are advanced topics. Let’s look at a sample to demonstrate its power:

在EL3.0中,支持高级查询语义,它结合了lambda表达式的力量,新的流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>

The table above will filter a backing list using the lambda expression passed

上表将使用传递的lambda表达式过滤一个后备列表

 <h:outputLabel id="avgLabel" for="avg" 
   value="Average of integer list value"/>
 <h:outputText id="avg" 
   value="#{['1','2','3'].stream().average().get()}"/>

The output text avg will compute the average of the numbers in the list. Both of these operations are null-safe by way of the new Optional API (another improvement on previous versions).

输出文本avg将计算出列表中数字的平均值。通过新的Optional API(对以前版本的另一项改进),这两种操作都是无效安全的。

Remember that support for this doesn’t require JDK 8, just JavaEE 7/EL3.0. What this means is that you’re able to do most of the JDK 8 Stream operations in EL, but not in the backing bean Java code.

记住,对此的支持不需要JDK 8,只需要JavaEE 7/EL3.0。这意味着你可以在EL中进行大部分JDK 8的Stream操作,但不能在backing bean的Java代码中进行。

Tip: You can use the JSTL <c:set/> tag to declare your data structure as a page-level variable and manipulate that instead throughout the JSF page:

提示:你可以使用JSTL <c:set/>tag来声明你的数据结构为页面级变量,并在整个JSF页面中操作它。

 <c:set var='pageLevelNumberList' value="#{[1,2,3]}"/>

You can now refer to “#{pageLevelNumberList}” throughout the page like it was a bona-fide JSF component or bean. This allows a significant amount of reuse throughout the page

现在你可以在整个页面中引用“#{pageLevelNumberList}”,就像它是一个真正的JSF组件或Bean。这允许在整个页面中进行大量的重用。

<h:outputText id="avg" 
  value="#{pageLevelNumberList.stream().average().get()}"/>

5. Static Fields and Methods

5.静态字段和方法

There was no support for a static field, method or Enum access in previous versions of EL. Things have changed.

在以前的EL版本中,没有对静态字段、方法或枚举访问的支持。事情已经发生了变化。

First, we have to manually import the class containing the constants into the EL context. This is ideally done as early as possible. Here we’re doing it in the @PostConstruct initializer of the JSF managed bean (A ServletContextListener is also a viable candidate):

首先,我们必须手动导入包含常量的类到EL上下文中。这最好是尽早完成。在这里,我们在JSF托管Bean的@PostConstruct 初始化器中完成(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");
         }
     });
 }

Then we define a String constant field (or an Enum if you choose) in the desired class:

然后我们在所需的类中定义一个String 常量字段(或者一个Enum ,如果你选择的话)。

public static final String constantField 
  = "THIS_IS_NOT_CHANGING_ANYTIME_SOON";

After which we can now access the variable in EL:

之后,我们现在可以在EL中访问该变量。

 <h:outputLabel id="staticLabel" 
   for="staticFieldOutput" value="Constant field access: "/>
 <h:outputText id="staticFieldOutput" 
   value="#{ELSampleBean.constantField}"/>

Per the EL 3.0 specification, any class outside of java.lang.* needs to be manually imported as shown. It’s only after doing this that the constants defined in a class are available in EL. The import is ideally done as part of the initialization of the JSF runtime.

根据EL 3.0规范,任何java.lang.*以外的类都需要手动导入,如图。只有在这样做之后,类中定义的常量才可以在EL中使用。导入最好是作为JSF运行时初始化的一部分来完成。

A few notes are necessary here:

这里有必要做一些说明。

  • The syntax requires that the fields and methods be public, static (and final in the case of methods)
  • The syntax changed between the initial draft of the EL 3.0 specification and the release version. So in some textbooks, you might still find something that looks like:
    T(YourClass).yourStaticVariableOrMethod

    This won’t work in practice (a design change to simplify the syntax was decided late into the implementation cycle)

    这在实践中是行不通的(为简化语法的设计变更在实施周期的后期才决定)。

  • The final syntax that was released still came out with a bug – it’s important to be running the latest versions of these.

6. Conclusion

6.结论

We’ve examined some of the highlights in the latest EL implementation. Major improvements were made to bring cool new features like lambda and streams flexibility to the API.

我们已经研究了最新的EL实现中的一些亮点。主要的改进是为了给API带来很酷的新特性,如lambda和流的灵活性。

With the flexibility that we now have in EL, it’s important to remember one of the design objectives of the JSF framework: clean separation of concerns with the use of the MVC pattern.

随着我们现在在EL中拥有的灵活性,重要的是要记住JSF框架的设计目标之一:通过使用MVC模式实现关注点的干净分离。

So it’s worth noting that the latest improvements to the API may open us up to anti-patterns in JSF because EL now has the capability to do real business logic – more so than before. And so it’s important to have that in mind during a real-world implementation, to make sure responsibilities are neatly separated.

所以值得注意的是,API的最新改进可能会使我们在JSF中出现反模式,因为EL现在有能力做真正的商业逻辑–比以前更多。因此,在现实世界的实施过程中,必须牢记这一点,以确保责任被整齐地分开。

And of course, the examples from the articles can be found on GitHub.

当然,文章中的例子可以在GitHub上找到