JSF式言語3.0のガイド

1概要

この記事では、Expression Languageバージョン3.0(EL 3.0)の最新機能、改善点、および互換性の問題について説明します。

これは、この記事の執筆時点で最新のバージョンであり、最新のJavaEEアプリケーションサーバーに同梱されています(JBoss EAP 7およびGlassfish 4は、それに対するサポートを実装した良い例です)。

この記事はEL 3.0のみの開発に焦点を当てています。式言語の一般的な詳細については、次のリンクを読んでください:/intro-to-jsf-expression-language[ELバージョン2.2]の記事。

2前提条件

この記事に示されている例は、Tomcat 8に対してもテストされています。 EL3.0を使用するには、次の依存関係を追加する必要があります。

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

3ラムダ式

最新のEL反復は、ラムダ式に対して非常に堅牢なサポートを提供します。ラムダ式はJava SE 8に導入されましたが、ELでのサポートはJava EE 7に付属しています。

ここでの実装はフル機能であり、ELの使用と評価に多くの柔軟性(およびいくつかの潜在的なリスク)を考慮しています。

3.1. ラムダEL値式

この機能を基本的に使用することで、EL値式の値型としてラムダ式を指定できます。

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

それを拡張して、Java SEのラムダ式の場合と同じように、複合ステートメントで再利用するためにELのラムダ関数に名前を付けることができます。複合ラムダ式はセミコロン( ; )で区切ることができます。

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

このスニペットは関数を cube 識別子に割り当てます。これはその後すぐに再利用可能になります。

3.2. λ式をバッキングBeanに渡す

これをもう少し進めてみましょう。ロジックをEL式に(ラムダとして)カプセル化し、それをJSFバッキングBeanに渡すことで、非常に柔軟になります。

<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);
}

これは、次のことを可能にする魅力的な機能です。

  • ロジックをパッケージ化するためのクリーンな方法

プログラミングパラダイム。上記のバッキングBeanロジックは、さまざまなソースから引き込まれた値に基づいて条件付きになる可能性があります。

  • JDK 8より前のコードベースでラムダサポートを導入する簡単な方法

アップグレードする準備ができていない可能性があります。

  • 新しいStreams/Collections APIを使用するための強力なツール。

4コレクションAPIの機能強化

以前のバージョンのELにおけるCollections APIのサポートはやや不足していました。 EL 3.0では、JavaコレクションのサポートにAPIの大幅な改善が導入されており、ラムダ式と同様に、EL 3.0はJava EE 7内でJDK 8ストリーミングのサポートを提供します。

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の Setと同様に、 要素の順序はリストに含まれていると予測できません。

  • 地図:

   <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 は、リスト内の数値の平均を計算します。これらの操作は両方とも、新しいリンクを使ってnull-safeです:/java-8-new-features[オプションAPI](以前のバージョンからのもう1つの改善)

これをサポートするのにJDK 8は必要なく、JavaEE 7/EL3.0だけが必要です。これが意味するのは、JDK 8の Stream 操作の大部分はELで実行できるが、バッキングBeanのJavaコードでは実行できないということです。

ヒント: JSTL <c:set/> タグを使用して、データ構造をページレベルの変数として宣言し、代わりにJSFページ全体でそれを操作できます。

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

正当なJSFコンポーネントまたはBeanのように、ページ全体で “#\ {pageLevelNumberList}” を参照できるようになりました。これにより、ページ全体でかなりの量の再利用が可能になります。

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

5静的フィールドとメソッド

以前のバージョンのELでは、静的フィールド、メソッドまたはEnumアクセスはサポートされていませんでした。世の中変わったんだよ。

まず、定数を含むクラスを手動で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");
         }
     });
 }

次に、希望のクラスに 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ランタイムの初期化の一部として行われます。

ここでいくつか注意が必要です。

  • 構文はフィールドとメソッドが public、static であることを要求します

(メソッドの場合は final ) ** 構文はEL 3.0の最初のドラフトの間に変わりました

仕様とリリースバージョンそれで、いくつかの教科書では、あなたはまだ何かのように見えるかもしれない:

T(YourClass).yourStaticVariableOrMethod

これは実際にはうまくいきません(構文を単純化するための設計変更は、 実施サイクルの後半に決定した) ** 最後にリリースされた構文にはまだバグがあります - それは

これらの最新バージョンを実行することが重要です。

6. 結論

最新のEL実装のハイライトをいくつか調べました。

ラムダやストリームの柔軟性のようなクールな新機能をAPIにもたらすために大きな改善が行われました。

私たちが現在ELで持っている柔軟性により、JSFフレームワークの設計目的の1つを覚えておくことは重要です:MVCパターンの使用による懸念の明確な分離

そのため、ELには実際のビジネスロジックを実行する機能が追加されているため、APIの最新の改善によりJSFのアンチパターンが可能になります。したがって、責任をきちんと分離していることを確認するために、実際の実装ではこの点に留意することが重要です。

そしてもちろん、記事からの例 https://github.com/eugenp/tutorials/tree/master/jsf GitHub。]