TECHNICAL NOTES · JAVA FRAMEWORK

Java Framework講義

土居俊彦氏によるJava Framework講義。MVC/MVC2、Struts、DI、AOP、Seasar2、S2Daoを演習とコード例で解説します。

MVC、DI、データアクセスの関係を表す抽象技術図

StrutsとSeasar

この講義は、JavaのWebアプリケーション開発を題材に、フレームワークの基礎概念、MVCとMVC2、Strutsによる画面制御、Seasar2によるDI・AOP・データアクセスを順に扱います。問題1から問題10までの演習を通じ、用語を覚えるだけでなく、自分で調べ、構成を理解するための教材です。

フレームワークの基礎概念

フレームワークとは

ソフトウェア開発におけるフレームワークは、さまざまなシステムで繰り返し必要になる処理を提供する土台やひな形です。共通部分をあらかじめ用意することで、個別のシステムでは固有の機能へ集中できます。その結果として、同じ処理を毎回作ることによる保守性の低下、工数の重複、新しいバグの作り込みを抑えることが期待されます。

ライブラリとの制御の違い

通常のライブラリは、プログラム側が必要な機能を呼び出します。フレームワークでは基本的な処理の流れをフレームワーク側が持ち、利用者が作った一部の処理をフレームワークが呼び出します。講義ではこの反転を「Don't call me! I call you.」という表現で説明していました。呼び出される個別処理の中からライブラリを使うこともできます。

アーキテクチャパターン

デザインパターンが主に設計や実装で繰り返し現れる解決方法を扱うのに対し、アーキテクチャパターンはアプリケーションの基本構造を扱います。多くのフレームワークは何らかのアーキテクチャパターンを実装しており、StrutsはMVC2を採用しています。

MVCとMVC2

Model・View・Controller

MVCは、アプリケーションをModel、View、Controllerに分割します。Modelはデータとビジネスロジックを持ち、ViewやControllerには依存しません。ViewはModelをどのように利用者へ見せるかを担当し、一つのModelに複数のViewを持たせることもできます。ControllerはViewからの入力をModelへ渡し、制御の流れを担います。

機能を三つの責務へ分けることで、それぞれの役割を明確にし、保守性と再利用性を高めます。この考え方はSmalltalkのアプリケーション開発で用いられ、後にさまざまな環境へ広がりました。

WebアプリケーションのMVC2

従来型のMVCでは、Modelの変更を、そのModelを参照するViewへ通知して描画できます。しかしWebアプリケーションでは、クライアントからの操作なしにサーバーが任意のタイミングで画面へデータを送ることはできません。そのため講義では、ControllerがModelの変更をViewへ伝えるWeb向けの構造をMVC2として説明しています。

Struts

構成要素

StrutsはJava向けのWebアプリケーションフレームワークで、MVC2のうち主にViewとControllerを扱います。JSPまたはHTML、ActionServlet、ActionForm、Action、struts-config.xmlが主要な構成要素です。

  1. JSPまたはHTMLから送られたデータをActionServletが受け取る。
  2. ActionServletがリクエストURIから実行するActionを決める。
  3. ActionFormへ入力値を設定し、必要な検証を行う。
  4. ActionがModelへのアクセスなどの処理を行い、ActionForwardで遷移先を決める。
  5. Action、ActionForm、遷移先の関係はstruts-config.xmlで定義する。

開発の流れ

講義では、まずHTMLのモックを作って利用者と機能を確認し、データベースを設計し、画面遷移図からURI、Action、ActionFormを決める流れを示しています。次にActionごとの詳細設計を行い、画面遷移図をもとにstruts-config.xmlを作成し、Actionを実装し、必要に応じてHTMLをJSPへ置き換えます。

web.xmlによる初期設定

Java Web Applicationの配備記述はweb.xmlに置きます。サーブレットコンテナはこの内容をもとにアプリケーションを読み込み、構成します。Strutsを使う場合はActionServletとURLパターンを登録します。

<servlet>
  <servlet-name>action</servlet-name>
  <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
  <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>action</servlet-name>
  <url-pattern>*.do</url-pattern>
</servlet-mapping>

ActionFormとValidation

ActionFormはorg.apache.struts.action.ActionFormを継承し、画面から送られるパラメータに対応したフィールドとgetter・setterを定義します。入力値の検証はActionFormで行う方法と、validation.xmlへ規則を書く方法があります。

public class LogonForm extends ActionForm {
  private String userid;
  private String passwd;
  public String getUserid() { return userid; }
  public void setUserid(String userid) { this.userid = userid; }
  public String getPasswd() { return passwd; }
  public void setPasswd(String passwd) { this.passwd = passwd; }
}

Actionと画面遷移

Actionは入力の検証、Modelへのアクセス、ActionForwardの決定を行います。ActionForwardは処理結果に応じた遷移先です。struts-config.xmlにはフォーム、URIごとのAction、入力画面、成功時と再試行時の遷移を定義します。

<action path="/LogonSubmit" type="ospi_sample.action.LogonAction"
        name="logonForm" scope="request" validate="true"
        input="/jsp/login_input.jsp">
  <forward name="retry" path="/jsp/login_input.jsp" />
  <forward name="success" path="/jsp/login_success.jsp" />
</action>

エラーメッセージなどはpropertiesファイルへ分離できます。ファイル名は固定ではなく、struts-config.xmlのmessage-resourcesで指定します。

Seasar2

DIコンテナ

Seasar2はDI(Dependency Injection)とAOP(Aspect Oriented Programming)を支える軽量コンテナです。DIコンテナは、定義情報などをもとにオブジェクトの生成と設定を担います。利用者がすべてをnewで組み立てる代わりに、コンテナが必要な依存関係を注入します。

DIはIoC(Inversion of Control、制御の反転)とも関係します。処理全体の制御をコンテナが担い、利用者は個々のコンポーネントを実装するという考え方で、フレームワークの「呼び出す側と呼び出される側の反転」に通じます。

AOPと横断的関心事

オブジェクト指向では継承を通じて処理を共通化できますが、継承関係を越えて複数の場所に現れる処理もあります。これを横断的関心事と呼びます。AOPは、散在する共通処理を一か所に定義し、コンパイル時やコンテナによる生成時に組み込めるようにします。講義では、ログ出力とトランザクション制御を例に挙げています。

diconファイル

Seasar2が管理するクラスやオブジェクトをコンポーネントと呼び、その情報をdiconファイルに記述します。規約を利用することで、定義をできるだけ少なくする工夫もありました。

<components>
  <include path="dao.dicon" />
  <component class="ospi_sample.dao.OspiUserDao">
    <aspect>dao.interceptor</aspect>
  </component>
</components>

S2Dao

S2Daoは、オブジェクトとリレーショナルデータベースの対応付けを行うO/R Mapperです。DTOとしてのJavaBeansと、DAOとしてのinterfaceを定義することで、データベースアクセスの実装量を減らします。

DTOとしてのJavaBeans

データベースのレコードに対応するJavaBeanを用意し、テーブルやカラムとの対応を定数またはJavaのアノテーションで記述します。講義の例ではSerializableを実装し、利用者IDとパスワードに対応するフィールドを持たせています。

DAOとしてのinterface

データアクセスの契約はinterfaceとして定義します。S2DaoではこのinterfaceにAspectを適用することで、問い合わせのための実装を個別に書かずにデータベースへアクセスする構成を示していました。

public interface OspiUserDao {
  Class BEAN = OspiUser.class;
  String getUser_ARGS = "userid";
  String getUser_QUERY = "userid = ?";
  OspiUser getUser(String userid);
}

演習問題

  1. MVC以外のアーキテクチャパターンを調べる。
  2. MVCパターンとObserverの関係を調べる。
  3. struts-config.xmlの要素を調べる。
  4. Struts Tilesの役割と使用方法を調べる。
  5. Strutsが提供する標準Actionを調べる。
  6. Seasar2以外のDIコンテナを調べる。
  7. Seasar2以外のAspectシステムを調べる。
  8. diconファイルの要素を調べる。
  9. S2Daoのアノテーションを調べる。
  10. S2Dao以外のO/R Mappingフレームワークを調べる。

最後に

講義では扱い切れなかった発展項目として、StrutsとSeasar2の連携、トランザクション制御、設定記述を減らす方法を挙げていました。Javaは実行環境の差を受けにくく、多くのオープンソースフレームワークが育ったため、StrutsとSeasar2だけにとどまらず、自分で調べて比較することを勧めて締めくくられています。

この資料は歴史的な技術講義として掲載しています。Javaの現在の学習情報はdev.java、仕様策定の仕組みはJava Community Processで確認できます。Java Kücheの勉強会全体は活動実績を参照してください。