Strutsと比較して理解するJSFとCDI、アクションベースとコンポーネントベースの違い:3つのフレームワークで学ぶエンタープライズJava開発入門(2)(2/3 ページ)
新規のエンタープライズJava開発において現在有力視される3つのフレームの違いについて解説する連載。今回から複数回に分けて、MVCのViewとControllerにフォーカスして各要素を紹介していきます。今回はJSFについて。サンプルコードを通じてJSFの機能を紹介し、JSFの特徴を3つ挙げた上で、JSFのメリット・デメリットを検討します。
「アクションベース」「コンポーネントベース」設計思想の違い
先ほどのサンプルコードを見ただけでは、JSFはボタンに呼び出すメソッドを記述して画面遷移するだけのフレームワークと思われたかもしれません。実際、簡単なアプリケーションを作るのであればその理解だけで構いません。
ですが、ボタン以外のイベント処理を行うようなJSFの機能を使いこなすためには、JSFとStruts 1の根本的な設計思想の違いを知っておく必要があります。両者の違いを一言で表現すると、「Struts 1はアクションベースのフレームワークで、JSFはコンポーネントベースのフレームワークだ」と言えるでしょう。
アクションベースとは
アクションベースとは、URLによってサーバ側で実行するアクション(プログラム)を決定し、アクションのレスポンスとしてHTMLなどのデータを返す方式です。
HTTPの基本的な仕組みであるリクエストとレスポンスにのっとったシンプルな構成であり、Servlet(サーブレット)やStruts 1、前回紹介したJAX-RS、Spring MVC、Play Frameworkなど多くのフレームワークはアクションベースに分類できます。
アクションベースは、構造が単純で理解しやすく、拡張もしやすいといった特徴があります。
コンポーネントベースとは
コンポーネントベースとは、HTTPを抽象化してViewを構成するパーツであるコンポーネントを中心に据えたフレームワークです。
先に示したサンプルコードでも、URLやHTTPリクエスト・レスポンスといったものが一切登場しませんでした。それは、JSFがHTTPを隠蔽(いんぺい)し、XHTMLに記述したコンポーネントの内容からViewの組み立てや、入力チェック、バッキングBeanへの入力値設定やメソッド呼び出しといった共通的な処理を内部で行っているためです。
コンポーネントベースは、Viewの再利用性を高め、コードの記述量を減らせるといった特徴があります。コンポーネントベースのフレームワークはJSFやApache Wicket、■http://www.atmarkit.co.jp/ait/articles/1303/26/news035.htmlASP.NETのWebForms■などが該当します。
アクションベースとコンポーネントベースの違い
アクションベースとコンポーネントベースの主な違いはViewにあります。アクションベースではViewはHTTPレスポンスに書き込むデータなので、HTMLの他、JSON、XMLなどさまざまな形式を扱えます。
コンポーネントベースではViewにコンポーネントを記述するといった決まりがあります。Controllerでもコンポーネントを前提としたコードを書く必要があります。
そのため、アクションベースは自由度が高く、WebアプリケーションやRESTベースのアプリケーションなど、適用範囲が広いといえます。しかし実装方法を自分で考える必要があります。
コンポーネントベースは実装ルールが決まっており、適用範囲も限定されます。実際JSFではHTMLベースのWebアプリケーションに限定されます。しかし、実装ルールに従えばコードの削減や再利用が望めます。
さて以降では、JSFならではの3つの特徴を紹介します。
【JSFの特徴1】柔軟なViewを作成できるFacelets
リスト1のXHTMLには、「h:form」のような独自のFacelets要素がありました。
JSFでは、よりHTMLに近い形式でXHTMLを書くことができます。リスト1を書き直したものが以下です。
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:jsf="http://xmlns.jcp.org/jsf"> <!-- 割愛 --> <h1>ログイン</h1> <form class="form" jsf:id="form"> <div> <span jsfc="h:messages" id="message"/> </div> <input type="text" jsf:value="#{loginAction.id}" placeholder=”user1を入力” /> <input type="password" jsf:value="#{loginAction.password}" /> <input type="submit" jsfc="h:commandButton" action="#{loginAction.login}" value="ログイン"/> </form> <!-- 割愛 --> </html>
リスト3に登場する要素は全てHTMLにあるものだけです。そのためデザイナーが作ったHTMLの変換も容易になります。どうやってFaceletsのコンポーネントを指定しているのかというと、いくつかのHTML要素内に書かれている「jsfc」属性と、「jsf:value」などの「jsf:」で始まる属性が鍵となります。
「jsfc」属性は、JSFコンポーネントを属性値に記述する方法で、HTML形式でViewを定義する基本的な方法です。
「jsf:」で始まる属性は「パススルーエレメント」と呼ばれます。これはJSF 2.2 から導入されたコンポーネントを自動認識する仕組みであると同時に、HTML5の属性を扱うための仕組みでもあります。自動認識に対応している要素は「TagDecorator(Java EE 7 Specification APIs)」を参照してください。
Viewの共通化の仕組み
Viewには、ヘッダやフッタなど、どの画面にも共通で出現する部分があったり、似たような記述を毎回しなければならないパーツのようなものがあったりします。Faceletsでは、前者に対してはテンプレート機能を、後者に対しては自作コンポーネントを提供することで、繰り返し登場するものを一元管理して再利用できるようにしています(この辺りの解説は割愛しますが、興味のある方はサンプルコードを参照してみてください)。
自作コンポーネント
JSFコンポーネントはオープンソースで多数公開されています。例えば、「Oracle ADF」「PrimeFaces」などがあります。
これらの自作コンポーネントを導入することで、リッチなViewを簡単に作成できるようになります。
【JSFの特徴2】イベント処理やAjax処理を簡単に使える
リッチなViewを作成しようとすると、ボタンを押したとき以外にも何らかの処理が必要となることがあります。
アクションベースのフレームワークでイベント処理を行うには、JavaScriptでAjaxを使用してデータを取得し、HTMLを変更するといったようなコードを書く必要があり、サーバサイド、クライアントサイドともに煩雑なコードが必要になります。
JSFではコンポーネントがイベントを検知したときに、バッキングBeanのメソッドを呼び出せるようになっており、その結果をJavaScriptのコードを書くことなしにAjaxで差分更新できるようになっています。
「3桁の国コードを入力すると、その隣に国名が表示される」という例で説明します。
まずは、Viewです。
<h:form> 国コード <h:inputText id="code" size="3" value="#{countryAction.code}" valueChangeListener="#{countryAction.find}"> <f:ajax execute="code" render="name" event="change"/> </h:inputText> :国名 <span jsf:id="name">#{countryAction.name}</span> </h:form>
ここでは入力項目に対して「valueChangeListener」という属性で、入力値が変わったときに実行するバッキングBeanのメソッドとして、「CountryAction」クラスのfindメソッドを指定しています。そして「f:ajax」タグでchangeイベントが起きたら、その結果をidが"name"のコンポーネントに反映するように指定しています。
このようにViewの中の一部だけを書き換える仕組みは、JSFがViewをコンポーネントの集合で管理しているために実現できています。
次に、バッキングBeanの内容です。
@RequestScoped @Named public class CountryAction { // 簡易的な国コードと国名のマップ private static final Map<String, String> map = new HashMap<String, String>(){{ put("JPN", "日本"); put("USD", "アメリカ"); }}; // Viewの入出力値 private String code; private String name; // 入力項目の変更イベントで呼ばれるメソッド public void find(ValueChangeEvent e) { // イベント変更値から表示用項目に国名を設定。 if (map.containsKey(e.getNewValue())) { name = map.get(e.getNewValue()); } else { name = ""; } } // アクセッサは省略 }
リスト5のfindメソッドは入力値の変更に伴って呼び出されるメソッドです。ValueChangeEventを引数に取り、変更前後の入力値を受け取れます。findメソッドでは、名称フィールドの値を更新するだけです。その後のViewの反映はXHTMLに記述してある内容に従って行われます。
このようにJSFが提供している機能に従うだけで、Ajax処理が簡単に実現できます。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- Struts後時代のJava EE/Javaモダン開発はどうあるべきか〜JJUG CCC 2014 Springまとめリポート(前編)
Java EEにおいてJava 8はどこまで利用できるのか、Java開発でGit、CI、継続的デリバリは、どこまで有効なのか、Struts後時代のJava EE開発における有効なフレームワークなどをお伝えする。 - 日本の開発者コミュニティが次世代Java仕様策定に貢献、Lambdaを手に入れたJavaテクノロジのその先へ
2015年4月8日、Javaテクノロジに関する開発者イベント「Java Day Tokyo 2015」が開催された。基調講演で紹介されたJavaテクノロジに関する話題を解説していきたい。 - Java 8時代の開発者のためのデバッグ/トラブル解決の基本・応用テクニック〜JJUG CCC 2014 Springまとめリポート(後編)
Java開発における3大トラブルと対策、IDEのデバッガー活用の必要性、Java 8より導入された新しいメモリ領域を使いこなすためのテクニック、独自のトランザクショナルメモリ機構を実装した有効性などをお伝えする。