■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2007年11月11日

    楽しいJava講座 - 初心者から達人へのパスポート
                  vol.078

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■


[このメールマガジンは、画面を最大化して見てください。]


========================================================
◆ 01.Tomcatのアプリケーション開発
========================================================

では、前回のソース・コードを解説していきましょう。

まず、AgeCalc(年齢計算をするBean)のソース・コードを見て
みましょう。

--------------------------------------------------------
package jp.co.flsi.lecture.webapp.bean;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class AgeCalc {
   private int dayOfBirth;
   private int age;

   public void setDayOfBirth(int dayOfBirth) {
      this.dayOfBirth = dayOfBirth;
   }

   public int getDayOfBirth() {
      return dayOfBirth;
   }
  

   public void setAge(int age) {
      this.age = age;
   }

   public int getAge() {
      return age;
   }

   public void calcAge() {
      Date today = new Date();
      DateFormat format = new SimpleDateFormat("yyyyMMdd");
      String stringToday = format.format(today);
      setAge((Integer.parseInt(stringToday) - getDayOfBirth()) / 10000);
   }
}
--------------------------------------------------------

このクラスは、dayOfBirthというプロパティーに生年月日を設定
しておいてから、calcAge()メソッドを実行すると、年齢計算を
行い、計算結果の年齢をageプロパティーに設定するようになって
います。

dayOfBirthプロパティーには生年月日をYYYYMMDDの形式で設定する
ことになっています。
これは、サーブレットを呼び出すためのWebページ(age.html)が、
生年月日をYYYYMMDDの形式で入力するように促しており、その
生年月日をサーブレットがAgeCalcのプロパティーに設定するよう
になっているためです。

このYYYYMMDD形式の数値は、年齢の計算に都合のよい形式です。
現在の年月日もYYYYMMDD形式の数値で表現してやれば、それらの
差が年齢の1万倍(+ 1万未満の端数)になりますから、それを1万
で割ってやれば(int型で計算すれば小数点以下が切り捨てられる
ので)ピッタリの年齢になります。

というわけで、現在の年月日をYYYYMMDD形式にするために使った
コードが

--------------------------------------------------------
Date today = new Date();
DateFormat format = new SimpleDateFormat("yyyyMMdd");
String stringToday = format.format(today);
--------------------------------------------------------

です。

ここで、Dateというクラス(java.utilパッケージにはいっている)
は、日付(時刻も含む)を表現するクラスですが、引数なしのコン
ストラクターでインスタンスを生成すると、現在の日付・時刻を
持ったインスタンスが生成されます。

DateFormatというクラスは日付(時刻も含む)の表示形式を表現
する抽象クラスで、そのサブクラスであるSimpleDateFormatには、
基本的な表示形式が用意されています。これによって、Dateのイン
スタンスが持つ日付(および時刻)を適切な表示形式の文字列に
変換(表示)することができます。
(逆に文字列からDateのインスタンスに変換することもできます。)

ここでは、現在の年月日をYYYYMMDD形式で表現したいわけですが、
そのためには、

SimpleDateFormat("yyyyMMdd")

という引数つきのコンストラクターを呼び出せばよいことになって
います。ここで、"yyyy"は数字4文字表現の年(ちなみに、2文字の
yyにすれば、2007年なら07といった数字2文字表現の年になります)、
"MM"は数字2文字表現の月(ちなみに、MMの代わりに小文字のmmを
指定すると、月ではなく秒が表示されてしまいます)、"dd"は数字
2文字表現の日を示すための引数のパターンです。
このコンストラクターに指定できる引数のパターンを詳しく知りた
い人は、APIリファレンスを参照してください。

このDateFormatのインスタンスを使ってDateをYYYYMMDD形式で表示
させるためには、format()メソッドを使って、Dateのインスタンス
を引数に指定し、

String stringToday = format.format(today);

のようにしてやればよいのです。

ただし、こうしてstringToday変数に取り込んだものは文字列です
から、そのままでは計算には使えません。そこで、例によって
IntegerのparseInt()メソッドを使ってint型への変換を行い、

setAge((Integer.parseInt(stringToday) - getDayOfBirth()) / 10000);

のようにして、年齢計算に使っているわけです。


残りのソース・コードはすべて理解できますね。




では、続いて、サーブレット(AgeCalcServlet)のほうを見てみま
しょう。

--------------------------------------------------------
package jp.co.flsi.lecture.webapp.servlet;

import java.io.IOException;
//import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jp.co.flsi.lecture.webapp.bean.AgeCalc;

public class AgeCalcServlet extends HttpServlet {

   public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
      AgeCalc calc = new AgeCalc();
      calc.setDayOfBirth(Integer.parseInt(request.getParameter("dayOfBirth")));
      calc.calcAge();
      request.setAttribute("AGECALC",calc);
      getServletContext().getRequestDispatcher("/ageresult.jsp").forward(request,response);
   }

}
--------------------------------------------------------

このサーブレットのdoPost()メソッドの初っ端のコード

AgeCalc calc = new AgeCalc();

では、先ほどのBean(AgeCalc)のインスタンスを生成していますね。
そして次のコード

calc.setDayOfBirth(Integer.parseInt(request.getParameter("dayOfBirth")));

では、そのインスタンス(calc)のdayOfBirthプロパティーに、Webブ
ラウザーから受け取った生年月日の値(YYYYMMDD形式のもの)を設定
しています。

続いて、そのインスタンス(calc)のcalcAge()メソッドを実行して
います。
calcAge()メソッドを実行すると、年齢計算が行われ、計算結果の年齢
がageプロパティーに設定されますね。

ここまでは、すらすらと理解できることと思います。


さて、続いてJSP(ageresult.jsp)を呼び出して、インスタンス(calc)
のageプロパティーの値をJSPに渡したいのですが、あいにくサーブレット
とJSPは互いに独立した存在なので、サーブレットの中で定義した変数
calcはJSPの中では認識できません。

では、どうやってこのインスタンスをJSPに渡すのかというと、その次の

request.setAttribute("AGECALC",calc);

というコードを使います。setAttribute()メソッドは、requestオブジェ
クト(HttpServletRequest型)に何らかの属性(オブジェクト)を設定
する(組み込む)ためのメソッドです。したがって、上記のコードが実行
されると、calcのインスタンス(calc変数に代入されているインスタンス)
がrequestオブジェクトに組み込まれることになります。
このとき、calc変数自体はJSPでは認識できませんから、代わりにインスタ
ンスの識別子(ID=identifier:変数名とは別の名前)を付けておきます。
上のコードでは"AGECALC"というのが識別子(ID)です。
つまり、setAttribute()メソッドの第一引数には属性(オブジェクト)の
名前(識別子)を、第二引数には属性のオブジェクト自身を指定します。

JSPが呼び出されるときにはサーブレットのrequestオブジェクト
(HttpServletRequest型)がそのままJSPに渡されますから、JSPでは、
上記の識別子を使ってrequestオブジェクトからcalcインスタンスを
取り出すことができます。


さて、そのJSPの呼び出しを行うのが、次のコード

getServletContext().getRequestDispatcher("/ageresult.jsp").forward(request,response);

です。
ここで、getServletContext()はServletContext(ServletContextは
サーブレットが実行される環境を表現するオブジェクトで、わかりやすく
言えばWebコンテナー(サーブレット・コンテナー)を表すオブジェクト)
を取り出すためのHttpServletのメソッド(正確に言うとHttpServletの
スーパークラスであるGenericServletのメソッド)で、
getRequestDispatcher()はRequestDispatcher(Webブラウザーからの要求
(requestオブジェクト)をサーブレットやJSPやHTMLファイルに渡して
それらの起動を行うためのオブジェクト)を取り出すためのServletContext
のメソッドです。
また、forward()はRequestDispatcherのメソッドで、requestオブジェクト
とresponseオブジェクト(HttpServletResponse型)を他のサーブレット
またはJSPに渡してそれら(サーブレットまたはJSP)を起動する(呼び出す)
ためのメソッドです。requestオブジェクトとresponseオブジェクトは、
それぞれ第一引数、第二引数として指定します。

上記のコードのように指定すると、ageresult.jspが呼び出され、
requestオブジェクトとresponseオブジェクトが渡されることになり
ます。
このとき、"/ageresult.jsp"のようにageresult.jspの前にスラッシュ(/)
が指定してあるのは、このageresult.jspがドキュメント・ルートと呼ばれ
る場所にあることを指定しているのですが、ドキュメント・ルートについて
は、後に本番環境のTomcatへのデプロイのお話をするときに詳しく説明いた
します。
現時点の段階では、ドキュメント・ルートは、Eclipseのワークスペースの
中のJStudy2(パッケージ・エクスプローラーの中のJStudy2)になっており、
上記の"/ageresult.jsp"という指定は、ageresult.jspというファイルが
JStudy2直下に置かれていることを指定するものです。



では、続いてageresult.jspのほうを見てみましょう。

--------------------------------------------------------
<HTML>
<HEAD>
<TITLE>年齢計算の結果のページ</TITLE>
</HEAD>
<BODY bgcolor="#77ffff" text="#fa5a00">
<H1>vol.077 [年齢計算の結果のページ]</H1>
<jsp:useBean class="jp.co.flsi.lecture.webapp.bean.AgeCalc" id="AGECALC" scope="request" />
<%= "あなたの年齢は" + AGECALC.getAge() + "歳です。"%>
</BODY>
</HTML>
--------------------------------------------------------

JSPでは、サーブレットによって(requestオブジェクトに組み込まれて)
渡されたBeanのインスタンスを認識する必要がありますが、そのために指定
したコードが

<jsp:useBean class="jp.co.flsi.lecture.webapp.bean.AgeCalc" id="AGECALC" scope="request" />

です。このjsp:useBeanというタグは(HTMLにはない)JSP独自のタグですが、
JSPに認識させたいBeanを指定するためのものです。

先ほどのサーブレットの

request.setAttribute("AGECALC",calc);

というコードで設定したインスタンスのID("AGECALC")をjsp:useBeanタグの
id属性に指定することによって、JSPでこのインスタンスを受け取ることが
できます。

jsp:useBeanタグでは、受け取るインスタンスの型を認識させるためにclass属性
を指定します。class属性にはパッケージ名とクラス名を

class="パッケージ名.クラス名"

の形式で指定します。
(class属性の代わりに他の属性を使う方法もあります。)

JSPは、jsp:useBeanタグに指定されたBeanについて、既存のインスタンスを
id属性とscope属性を照合して探し、見つかればそれを使いますが、見つから
なければ新たにBeanのインスタンス生成を行います。

このscope属性はインスタンスを探す場所の範囲およびインスタンスの寿命を
指定するもので、"page", "request", "session", "application"の4種類の値
を指定できますが、今回のようにrequestオブジェクトの属性としてインスタ
ンスを組み込む場合は、"request"を指定しておきます。

なお、scope属性の省略時にはデフォルトとして"page"が指定されたことに
なってしまいますので、scope属性は通常は省略せずに明示する必要があり
ます。

ちなみに、これらの値は、それぞれ以下のような意味を持ちます。
(詳細は後述します。)

--------------------------------------------------------
"page" = JSPのページの範囲。Beanのインスタンスの寿命はページが終了
するまでとなります。

"request" = クライアント(Webブラウザー)からの要求(request)の範囲。
インスタンスはrequestオブジェクトの属性から探し出されることになります。
サーブレットやJSPからforward()されたrequestオブジェクトはそのまま維持
されます。クライアントにresponseを返してしまえば、寿命は終わります。

"session" = セッション(後述)が続く間がインスタンスの寿命となります。
インスタンスはHttpSessionオブジェクトから探し出されます。

"application" = インスタンスはアプリケーションにともなって生成され、
アプリケーションが終了するまでが寿命となります。インスタンスは
ServletContextオブジェクトの属性として探し出されます。
--------------------------------------------------------


さて、こうして見つけられたBeanのインスタンスは、そのIDを通してアクセス
することができます。たとえば、次のコード

<%= "あなたの年齢は" + AGECALC.getAge() + "歳です。"%>

の中のAGECALC.getAge()は、ID(AGECALC)を通してアクセスしたインスタ
ンスのgetAge()メソッドを呼び出しています。
これによって、JSPが生成するWebページにこのBeanのageプロパティーの値
(計算結果の年齢)を埋め込むわけです。



残りのage.htmlについては、特に説明しなくても理解できることと思います。



以上で、これらのサーブレットとBeanとJSPを使ったWebアプリケーション
の連携プレイの様子はすべて理解できたことと思います。


なお、JSPの仕様の詳細を独力で調べたい人は、

http://java.sun.com/products/jsp/

の中から「Final Release of JavaServer Pages Specification」などの
リンクをたどれば、仕様(specification)のPDFファイルをダウンロード
することができます。




次回からは、少し複雑なアプリケーションの例として、インターネット・
ショッピングのアプリケーションを作っていくことにしましょう。


今回はここまでにします。
何か、わからないところがありましたら、下記のWebページまで質問を
お寄せください。


(続く)



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2007 Future Lifestyle Inc. 不許無断複製