サーブレット+JSP+データクラス/Beanを使ったWebアプリケーションで、オブジェクト指向プログラミングを行う方法について解説します。
一般的なWebアプリケーションとして、以下のようなオブジェクトモデルを使って解説します。
このようなモデルとしては、顧客管理システム、顧客の応対を行うコールセンタシステム、課金請求処理を行うビリングシステムなどがあります。
これらのシステムでは、ログインオブジェクトにより、操作者(オペレータ)を特定し、顧客検索オブジェクトにより、対象となる顧客を特定します。
顧客情報、応対情報、課金情報などは、ある顧客に対する処理のみを行います。
| ログインオブジェクト | 操作者がログインし、操作者IDとアクセス権の設定を行うオブジェクト |
|---|---|
| フレーム管理オブジェクト | 各オブジェクトの画面構成など、プログラム全体を管理するオブジェクト |
| 顧客検索オブジェクト | いろいろな条件で絞込み、対象となる顧客IDを特定するオブジェクト |
| 顧客情報オブジェクト | 顧客の個人情報を表示・更新するオブジェクト |
| 応対情報オブジェクト | 操作者の顧客に対する応対内容を表示・更新するオブジェクト |
| 課金情報オブジェクト | 顧客の月単位の課金情報や利用情報を表示・更新するオブジェクト |
Webブラウザでは、ウィンドウまたはフレーム単位で、HTTPプロトコルによって通信されます。 Webアプリケーションでは、この単位で、サーブレットが割り当てられますので、オブジェクトの分類もウィンドウまたはフレーム単位で行います。
| フレーム管理オブジェクト (IndexServlet + Index.jsp)
|
JavaのWebアプリケーションは、サーブレット、データクラス/Bean、JSPから構成されます。 これらの関係は、オブジェクト指向のMVCモデルで表されます。 サーブレット(コントローラ)からデータクラス/Bean(モデル)のメソッドを使ってセッション変数に格納し、forwardしてJSP(ビュー)を呼び出すことで画面が表示されます。
1つのオブジェクトに対して、1つのデータクラス/Beanと1つのサーブレットを対応させます。
しかし、1つのオブジェクトを操作する画面は複数ありますので、複数のJSPファイルが必要となります。
そこで、サーブレットが表示する画面を識別するため、画面ID(scr)を設定します。
この画面IDによって、サーブレットはどの画面からの指示かを知ることができます。
画面を共通化することによって、同じJSPでも、新規登録と更新や、詳細表示と削除などのように、複数の画面として利用されることがあります。
このような場合にも、サーブレットは画面IDによって画面の機能を識別することができます。
また、画面上のどのボタンをクリックしたかを識別するため、アクションID(act)を設定します。
アクションIDは、「検索」「登録」「削除」などのボタン毎に設定します。
1つの画面に、複数のフレームが存在する場合には、フレームIDを設定するか、アクションIDを画面内でユニークにする必要があります。
1つの画面に、ボタンが1つしか存在しない場合は、アクションIDは不要となります。
サーブレットは、GETメソッドでもPOSTメソッドでも動作するように設計しているため、serviceメソッドをオーバーライドしています。
ここでは、フレーム管理オブジェクト、顧客検索オブジェクト、顧客情報オブジェクトを使ったサンプルを例に解説します。
顧客検索画面では、いろいろな条件で絞込み、顧客IDを特定します。 このサンプルでは、簡単にするため顧客ID(001)を直接入力します。
顧客検索画面のOKボタンを押すと、顧客情報オブジェクトに、顧客IDが変更されたことを通知します。 また、フレーム管理オブジェクトに対しても、顧客IDが変更されたことを通知し、画面を再表示します。 本来は、顧客IDをキーにしてデータベースを検索して表示しますが、このサンプルでは、顧客IDが001の場合に定数を表示しています。
顧客検索画面と顧客情報画面を表示する際のシーケンス図を以下に示します。 顧客検索サーブレットと顧客情報サーブレットは、フレーム管理サーブレットから直接呼ばれるわけではなく、フレームオブジェクトが表示した画面(Index.jsp)によって、ブラウザから呼び出されます。
顧客検索画面から、別フレームの顧客情報画面を更新する際のシーケンス図を以下に示します。
サーブレットでは、自分のウィンドウまたはフレームの更新権限しかありません。
従って、サーブレットの処理結果によって、他のウィンドウやフレームの画面を更新したい場合は、出力するJSP内のJavaScriptのメソッドを使って、ブラウザから呼び出す必要があります。
サーブレットの処理結果ではなく、JSPの画面で遷移する場合は、以下の方法で簡単に画面遷移できます。
フレームを使っている画面で、JSPから画面全体を書き換える場合は、target属性で _parent指定をして親のサーブレットを呼び出します。 このとき_topを利用してはいけません。 _topは絶対位置指定ですので、オブジェクトがリロケータブルでなくなってしまいます。
<a href="IndexServlet" target="_parent">画面切替</a> <form action="IndexServlet" target="_parent"> <input type="submit" value="画面切替" /> </form> |
target属性を使わないで、JavaScriptで画面全体を書き換える方法もあります。 この場合も、topを使わずに、parentを使います。 フレームではなく、サブウィンドウの場合は、parentではなく、openerを使う必要があります。
<a href="javascript:parent.location.href='IndexServlet'">画面切替</a> |
パッケージ名と、ソースファイルの格納ディレクトリは、以下のようにします。
| パッケージ名 | jp.ash.webapp |
|---|---|
| JSPディレクトリ名 | /home/WebApp/src/ |
| Javaソースディレクトリ名 | /home/WebApp/src/jp/ash/webapp/ |
サンプルプログラムの主なファイル構成です。
画面定義用のJSPファイルと、データ設定用、制御用のJavaソースファイルは分けて作成します。
JavaScriptのソースは、*.jsファイルとすることもできますが、このサンプルではJSPファイル内に記述しています。
| オブジェクト名 | ファイル名 |
|---|---|
| フレーム管理オブジェクト |
IndexServlet.java IndexData.java(操作者IDを保存しないため無し) Index.jsp Index.js(Index.jspに含まれるため無し) |
| 顧客検索オブジェクト |
SearchServlet.java SearchData.java Search.jsp Search.js(Search.jspに含まれるため無し) |
| 顧客情報オブジェクト |
CustServlet.java CustData.java Cust.jsp Cust.js(無し) |
Index.jspでは、フレームセットを構成しています。
このソースがブラウザに送信されると、ブラウザが、SearchServletとCustServletを呼び出します。
また、画面を切り替えるためのJavaScriptのメソッドを定義しています。
画面が切り替わっても、Index.jspは更新されないので、システム共通のメソッドなどを定義しておくと便利です。
| Index.jsp |
|---|
<%@ page contentType="text/html; charset=Shift_JIS" %>
<html>
<head>
<script type="text/javascript" language="javascript"><!--//
function chgCust(id) {
cust.location.href = 'CustServlet?id=' + id;
}
//--></script>
</head>
<frameset rows="120,*">
<frame src="SearchServlet" name="search">
<frame src="CustServlet" name="cust">
</frameset>
</html>
|
IndexServletでは、Searchオブジェクトを作成し、forwardによって、画面定義用のJSPファイル(Index.jsp)を呼び出しています。
| IndexServlet.java |
|---|
package jp.ash.webapp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** フレーム管理サーブレット **/
public class IndexServlet extends HttpServlet {
public void service (HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// 顧客検索画面の表示
req.getRequestDispatcher("Index.jsp").forward(req, res);
}
}
|
Search.jspでは、顧客検索画面を表示しています。
検索条件を入力すると、SearchServletを呼び出します。
また、顧客IDが変更されたときに、顧客情報画面を更新するため、JavaScriptのchgCustメソッドを呼び出しています。
| Search.jsp |
|---|
<%@ page contentType="text/html; charset=Shift_JIS" %> <jsp:useBean id="search" class="jp.ash.webapp.SearchData" scope="session" /> <html> <head> <script type="text/javascript" language="javascript"><!--// <%=search.script%> //--></script> </head> <body> <h1>顧客検索</h1> <form name="search" action="SearchServlet"> 顧客ID: <input type="text" name="id" value="<%=search.custId%>" /> <input type="submit" value="検索" /> </form> </body> </html> |
SearchServletでは、カレントの顧客IDを保存し、顧客情報オブジェクトのchgIdメソッドを呼び出します。 また、forwardによって、JSPファイル(Search.jsp)を呼び出し、顧客検索画面を再表示しています。 SearchServletでは、顧客情報の画面だけを更新することはできないため、JSPファイルにJavaScriptを定義することによって書き換えています。
| SearchServlet.java |
|---|
package jp.ash.webapp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** 顧客検索サーブレット **/
public class SearchServlet extends HttpServlet {
public void service (HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
HttpSession session = req.getSession();
req.setCharacterEncoding("Shift_JIS");
// Searchオブジェクトの作成
SearchData search = (SearchData) session.getAttribute("search");
if (search == null) { search = new SearchData(); }
session.setAttribute("search", search);
// 顧客IDの設定
String id = req.getParameter("id");
if ((id != null) && (!id.equals(search.custId))) {
search.custId = id;
search.script = "parent.chgCust('" + id + "');";
}
// 顧客情報画面の表示
req.getRequestDispatcher("Search.jsp").forward(req, res);
search.script = "";
}
}
|
顧客検索データは、データクラス/Beanとして作成します。
一般的には、getXXXメソッドやsetXXXメソッドを持つ、データクラス/Beanとして作成します。
このサンプルでは、getメソッドやsetメソッドは作成せずに、データクラスのpublicフィールドを直接、参照更新しています。
sessionスコープとして設定することにより、同一のセッション内で有効な変数としています。
| SearchData.java |
|---|
package jp.ash.webapp;
/** 顧客検索情報 **/
public class SearchData {
/** 検索した顧客ID **/
public String custId;
/** ブラウザで実行するスクリプト **/
public String script;
}
|
Cust.jspでは、顧客情報表示画面を表示しています。
| Cust.jsp |
|---|
<%@ page contentType="text/html; charset=Shift_JIS" %> <jsp:useBean id="cust" class="jp.ash.webapp.CustData" scope="request" /> <html> <body> <h1>顧客情報表示</h1> <table border="1"> <tr><th>項目名</th> <th>項目内容</th></tr> <tr><th>顧客ID</th> <td><%=cust.id%></td></tr> <tr><th>顧客名</th> <td><%=cust.name%></td></tr> <tr><th>郵便番号</th><td><%=cust.zipcode%></td></tr> <tr><th>住所</th> <td><%=cust.address%></td></tr> <tr><th>電話番号</th><td><%=cust.tel%></td></tr> </table> </body> </html> |
CustServletでは、forwardによって、画面定義用のJSPファイル(Cust.jsp)を呼び出しています。
| CustServlet.java |
|---|
package jp.ash.webapp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** 顧客情報サーブレット **/
public class CustServlet extends HttpServlet {
public void service (HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
HttpSession session = req.getSession();
req.setCharacterEncoding("Shift_JIS");
// Custオブジェクトの作成
CustData cust = (CustData) session.getAttribute("cust");
if (cust == null) { cust = new CustData(); }
session.setAttribute("cust", cust);
// パラメータの取得
String id = req.getParameter("id");
if ((id != null) && (!id.equals(cust.id))) {
cust.chgId(id);
}
// 顧客検索画面の表示
req.setAttribute("cust", cust);
req.getRequestDispatcher("Cust.jsp").forward(req, res);
}
}
|
顧客情報データは、データクラス/Beanとして作成します。
サーブレットでは、sessionスコープとして設定していますが、JSPを呼び出す場合には、requestスコープにコピーしています。
これは、同一オブジェクトの画面を複数表示する場合を考慮しているためです。
JSPで、scope="request"を指定することで、同一リクエスト内で有効な変数となります。
このサンプルでは、データベースを検索せずに、顧客IDが001の場合に、定数を設定しています。
| CustData.java |
|---|
package jp.ash.webapp;
/** 顧客データ **/
public class CustData {
/** 顧客ID **/
public String id;
/** 顧客名 **/
public String name;
/** 郵便番号 **/
public String zipcode;
/** 住所 **/
public String address;
/** 電話番号 **/
public String tel;
/** 顧客データの初期設定 **/
CustData () {
id = " ";
name = " ";
zipcode = " ";
address = " ";
tel = " ";
}
/** 顧客IDの変更(顧客データ検索) **/
public void chgId(String id) {
this.id = id;
if ((id != null) && (id.equals("001"))) {
name = "Joe Masumura";
zipcode = "920-0967";
address = "金沢市菊川1-9-6";
tel = "076-261-4921";
} else {
id = " ";
name = " ";
zipcode = " ";
address = " ";
tel = " ";
}
}
}
|
サーブレットの登録は、WEB-INF/web.xmlファイルで行います。
| web.xml |
|---|
<?xml version="1.0" ?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>ASH Sample Web Application</display-name>
<servlet>
<servlet-name>IndexServlet</servlet-name>
<servlet-class>jp.ash.webapp.IndexServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>SearchServlet</servlet-name>
<servlet-class>jp.ash.webapp.SearchServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>CustServlet</servlet-name>
<servlet-class>jp.ash.webapp.CustServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>IndexServlet</servlet-name>
<url-pattern>/IndexServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SearchServlet</servlet-name>
<url-pattern>/SearchServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>CustServlet</servlet-name>
<url-pattern>/CustServlet/*</url-pattern>
</servlet-mapping>
</web-app>
|