第1回 開発環境の準備〜Bean定義ファイルの作成
最終更新日:2007/05/30
Spring Framework(以下Springと記述します)はオープンソースの代表的なDIコンテナです。DIコンテナではSpring以外にもSeasar2や最近登場したGuiceなど様々な製品がありますが、Springはその中でも最も広く使われているものの一つでしょう。
Springは単にDI(Dependency Injection)の機能を提供するだけでなく、AOPやデータベースアクセス、Webとの連携をサポートするオールインワンのフレームワークです。実際DIが可能であるからという理由だけでなく、その周辺をサポートする機能まで含めた総合的な評価によって、採用するかどうかを判断することが多いのではないでしょうか。
この連載では簡単なデータアクセスを行うアプリケーションの作成を通じて、Spring 2.0のアプリケーション開発方法を紹介していく予定です。
DIコンテナはオブジェクト間の依存関係を構築するためのコンテナです。2つのオブジェクト間に依存関係を持たせる場合は、通常newなどによって一方のオブジェクトから他方のオブジェクトを生成することによって行います。しかしこのように直接オブジェクトを生成するやり方では、基本的にはソースコードを変更しない限り依存関係を変えることができません。そのためオブジェクトを入れ替えてシステムの動作を変更させることが難しくなります。特に単体テストでテスト用のスタブを実際のオブジェクトと入れ替えるといったことが簡単にできないため、単体テストの効率低下やシステムの品質低下を招く要因にもなりかねません。
DIコンテナはこういったオブジェクト間の依存関係を構築するための仕組みを提供してくれます。依存関係を構築するために設定ファイルやアノテーションなどが使われることが多いようです(SpringではBean定義ファイルで行います)。設定ファイルやアノテーションを変更することにより、オブジェクト間の依存関係をできる限りシステムに影響を与えないように変更することができるため、将来の拡張に強くなり、またテストを効率的に行うことができるというメリットがあります。
AOP(Aspect Oriented Programming)はアスペクト指向プログラミングの略称です。オブジェクト指向でのクラス設計を行っても、ログ出力やトランザクションといった複数のクラスに横断的に表れる処理がどうしてもでてきます。AOPではこういった横断的な処理(関心事)をアスペクトとして抽出し、元のソースコードに手を加えることなく、新たな処理を追加することができます。
この連載でもログ出力やトランザクションをSpring AOPを使って実装する予定です。
http://www.springframework.org/downloadよりSpringの媒体をダウンロードします。2007/05/11現在の最新版は2.0.5です。バイナリモジュールとしてspring-framework-2.0.x.zipとspring-framework-2.0.x-with-depencies.zipがあります。後者はSpringの依存ライブラリがすべて含まれているので、後者をダウンロードします。
ダウンロード後展開し、適当なディレクトリに配置します。
Spring IDEはspringide.orgで開発されているEclipse向けプラグインです。Springを使った開発ではBean定義ファイルに様々なBeanを登録することになります。そのためBean定義ファイルの検証機能や入力支援機能は支援はSpringアプリケーションの開発において必須と言えるのではないでしょうか。SpringIDEはそういった機能を提供してくれます。
Spring IDEは1.x系と2.x系があり、Spring 2.0に対応するのは2.x系です。以下はSpring IDE 2.0の主な機能です。
要素名、属性名、クラス名、Bean名を補完できます。XMLスキーマにも対応しているので、Springを使う場合は是非とも使いたい機能です。
存在しないクラスや参照名を記述した場合にエラーを通知してくれます。エラー箇所はXMLエディタの左側に赤い×が表示され、×をポイントすることでエラーの内容を知ることができます。
Bean定義ファイル上のクラス名をCtrlを押しながら選択することで、該当のソースファイルを表示することができます。
パッケージ・エクスプローラ・・・ではなくプロジェクト・エクスプローラとの統合です。Spring IDE 2.0はプロジェクト・エクスプローラと統合されており、プロジェクト・エクスプローラ上でBeanの参照関係がわかるようになっています。パッケージ・エクスプローラでも、どのクラスがBeanとして定義ファイルに登録されているかわかりますが(アイコンにSマークが付く)、Beanの参照関係まではわからないので、Spring IDE 2.0ではプロジェクト・エクスプローラを使用することになるようです。
プロジェクト・エクスプローラはEclipseのメニューからウィンドウ>ビューの表示>その他>一般から選択します。実際にプロジェクト・エクスプローラを表示してみると、Bean定義ファイルに登録されたクラスのBean名や、そのBeanが参照しているBeanなどがわかります。以下の例で、「referenced by」以下の内容がそれにあたります。 尚、referenced by以下の項目をダブルクリックすると、該当するBean定義ファイルの場所が開かれます。
メニューからウィンドウ>ビューの表示>その他>Spring>Beans Explorerを選択すると、Springプロジェクトと登録されたBean定義ファイル一覧が表示されます。ここでBean定義ファイルを選択して右クリック>Show Graphを選択すると、Bean定義ファイルの内容がグラフ表示されます。
Bean定義ファイルやアノテーションによりAOPを適用すると、エディタにAOPのマーカが表示されます。以下はポイントカットが設定されたメソッドの例です。左側にそれを示すアイコンが表示されていることがわかります。

Spring IDE 2.0の動作にはEclipse 3.2以上が必要ですので、まずはEclipse3.2をインストールします。言語パックもあてておきましょう。ここではEclipseのインストール手順は割愛します。
ではSpring IDE 2.0をインストール・・・と行きたいところですが、2007/05/11 現在、Spring 2.0に対応したSpring IDE 2.0は正式リリースされていません。XMLスキーマでなくDTDの文書型定義を使ってBean定義ファイルを記述する場合は1.x系でも問題ありませんが、Spring 2.0ではXMLスキーマによって設定ファイルの記述が簡単になっているので、できれば2.x系のIDEを使いたいところです。
そのため今回はSpring IDE 2.0のMilestoneビルドを使用することにします。2007/05/11現在の最新版は2.0M3です。Spring IDEのRoadmapによると、2.0のリリースは2007/06/21 が予定されているようですが、2.0M4のリリースが遅れているため、リリース時期は延びるのかもしれません。
Spring IDEをEclipseのソフトウェア更新によってインストールします。Eclipseのメニューからヘルプ>ソフトウェア更新>検索およびインストール>インストールする新規フィーチャーを検索>新規リモートサイトで、更新サイトのURLをhttp://springide.org/updatesite_dev/とします。Spring IDE 2.0が正式リリースされた後ならば、URLを正規UpdateサイトのURLに変更して下さい。
SpringIDEの更新サイトを登録したら、下図のようにCallistoディスカバリー・サイトにもチェックを入れて、終了を選択します。
次にインストール対象を選択します。今回はSpring IDE 2.0 M3をインストールするので、SpringIDE_devの中のM3を選択します。すると依存プラグインが無いというエラーが出ますが、「必須項目を選択」を選択すると、Callistoディスカバリー・サイトから必要なプラグインが自動で選択されます。Spring IDE 2.0はWTP1.5を必要とするので、それに関連するプラグインが選択されていることが下の図を見るとわかります。
「次へ」を選択し、画面の指示に従ってインストールを完了させます。
今回作成する予定のサンプルプログラムではデータベースアクセスを行うため、事前にデータベースを用意します。今回はMySQL5.0(For Windows)を使用することにしますが、他のデータベースでもかまいません。以下ではMySQL5.0のインストール手順を簡単に説明します。
http://www.mysql.com/からMySQL Community Serverを入手します。インストールするプラットフォーム毎にバイナリが提供されているので、適切なバイナリを選択して下さい。2007/05/11現在の最新版は5.0.41です。ここではWindows向けのインストーラ付きバイナリ(mysql-5.0.41-win32.zip)をダウンロードしたものとします。
ダウンロードしたファイルを展開し、Setup.exeを実行します。デフォルト設定のままウィザードを進めてください。ただし文字コードの設定(下図)では「Best Support For Multilingualism」を選択することにします。これでデフォルトの文字コードがutf-8になります。
rootユーザのパスワードも設定します。
ウィザードが終わると、MySQLがWindowsサービスとして登録され、既に開始された状態になっているはずです。Windowsサービスは自動起動するよう設定されているため、手動起動に切り替えておいた方が良いでしょう。
MySQLのGUI管理ツールやクエリブラウザを含むGUIツールです。これらもインストールしておきましょう。http://dev.mysql.com/downloads/mysql/5.0.htmlの左のメニューからGUI Toolsを選択し、各プラットフォーム向けのバイナリをダウンロードしてインストールして下さい。
インストール完了後、スタート>すべてのプログラム>MySQL>MySQL System Tray Monitorを選択すると、MySQLサーバの起動/終了や管理ツールを起動するためのアイコンがタスクトレイに登録されます。これを使うと便利です(下図)。
同じくhttp://dev.mysql.com/downloads/mysql/5.0.htmlの左のメニューからConnectors>Connector/Jを選択してJDBCドライバを入手します。ダウンロード後展開し、適当なディレクトリに配置して下さい。
sampleスキーマを作成したら、次はMySQL Query Browserを開き、以下のSQLを実行して2つのテーブルを作成します。
CREATE TABLE `sample`.`bumon` ( `CD_BUMON` varchar(6) NOT NULL, `NM_BUMON` varchar(45) NOT NULL, PRIMARY KEY (`CD_BUMON`) ) CREATE TABLE `sample`.`shain` ( `CD_SHAIN` varchar(6) NOT NULL, `NM_SHIMEI` varchar(20) NOT NULL, `YMD_NYUUSHA` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, `CD_BUMON` varchar(6) NOT NULL, PRIMARY KEY (`CD_SHAIN`), KEY `FK1` (`CD_BUMON`), CONSTRAINT `FK1` FOREIGN KEY (`CD_BUMON`) REFERENCES `bumon` (`CD_BUMON`) )
社員テーブルと部門テーブルです。社員テーブルの部門コード(CD_BUMON)には外部キーを設定してあります。これでデータベースの準備は完了です。
それではEclipseを起動し、新規にSpringプロジェクトを作成します。メニューからファイル>新規>プロジェクトを選択すると、以下のようにSpring Projectが登録されているので、これを選択してプロジェクトを作成します。Springプロジェクトを作成すると、パッケージ・エクスプローラなどでプロジェクトのアイコンにSマークが付きます。
Spring IDE 2.0M3では、作成したSpringプロジェクトにJRE システム・ライブラリが設定されず、そのままではJavaソースのビルドに失敗します。Springプロジェクトを作成したら、プロジェクトを右クリック>プロパティ>Javaのビルド・パス>ライブラリータブを選択し、「ライブラリーの追加」からJRE システムライブラリーを手動で追加して下さい。2.0リリース時にはこれも改善されているのではないかと思います。
今回作成するサンプルプログラムは以下のようなクラス構成です。
社員テーブル(Shain)、部門テーブル(Bumon)へのCRUD(Create/Read/Update/Delete)操作を持つDAO、テーブルの1レコードのデータを保持するDTO、そしてDAOへのアクセスを行うServiceです。ServiceはDaoを利用するため、DAOに依存することになります。この依存関係をDIにより設定します。
Spring 2.0ではBean定義ファイルがXMLスキーマに対応しました(Spring1.xのDTDにも対応しているため、これまで通りDTDによる文書型定義を使うこともできます)。特にAOPとトランザクションの設定が簡単に記述できるようになっています。
目的毎にスキーマが分かれており、Bean定義ファイル内で使用するスキーマをxmlns:スキーマ名というように宣言する必要があります。以下はaopスキーマ, txスキーマ(トランザクション)を使用する場合のBean定義ファイルの例です。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
">
</beans>
ここで上記のクラス図に登場するインタフェースとクラスを作成します。DAOの実装はこの時点では空実装にしておきます(Bean定義ファイルへ登録することが目的のため)。DAOはCRUD + FindAll、Serviceは登録/削除/取得というメソッドを基本的に宣言しています。それぞれのインタフェースを以下に示します。
IBumonDao.java
package dao;
import java.util.List;
import dto.Bumon;
public interface IBumonDao {
void insert(Bumon bumon);
Bumon load(String cdBumon);
void update(Bumon bumon);
void delete(String cdBumon);
List<Bumon> findAll();
}
IShainDao.java
package dao;
import java.util.List;
import dto.Shain;
public interface IShainDao {
void insert(Shain shain);
Shain load(String cdShain);
void update(Shain shain);
void delete(String cdShain);
List<Shain> findAll();
}
IBumonService.java
package service;
import java.util.List;
import dto.Bumon;
public interface IBumonService {
void registBumon(Bumon bumon);
Bumon getBumon(String cdBumon);
List<Bumon> getAllBumon();
void removeBumon(String cdBumon);
}
IShainService.java
package service;
import java.util.List;
import dto.Shain;
public interface IShainService {
void registShain(Shain shain);
Shain getShain(String cdShain);
List<Shain> getAllShain();
void removeShain(String cdShain);
}
ISetupService.java
package service;
import dto.Bumon;
import dto.Shain;
public interface ISetupService {
void setup(Bumon b, Shain s);
}
DAOの実装は以下のように何もしない or nullを返すだけです(今のところ)。Serviceの実装ではDAOをDIで設定するため、使用するDAOをフィールドに持ち、そのフィールドに対してGetter/Setterメソッドを定義します。メソッドの実装は単にDAOのメソッドを呼び出しているものがほとんどです。ShainDao, ShainServiceはBumonとほぼ同じなので省略します。
BumonDao.java
package dao;
import java.util.List;
import dto.Bumon;
public class BumonDao implements IBumonDao {
public void insert(Bumon bumon) {}
public Bumon load(String cdBumon) {
return null;
}
public void update(Bumon bumon) {}
public void delete(String cdBumon) {}
public List<Bumon> findAll() {
return null;
}
}
BumonService.java
package service;
import java.util.List;
import dao.IBumonDao;
import dto.Bumon;
public class BumonService implements IBumonService {
private IBumonDao bumonDao;
public void registBumon(Bumon bumon) {
bumonDao.insert(bumon);
}
public Bumon getBumon(String cdBumon) {
return bumonDao.load(cdBumon);
}
public List<Bumon> getAllBumon() {
return bumonDao.findAll();
}
public void removeBumon(String cdBumon) {
bumonDao.delete(cdBumon);
}
public IBumonDao getBumonDao() {
return bumonDao;
}
public void setBumonDao(IBumonDao bumonDao) {
this.bumonDao = bumonDao;
}
}
SetupService.java
package service;
import dao.IBumonDao;
import dao.IShainDao;
import dto.Bumon;
import dto.Shain;
public class SetupService implements ISetupService {
private IBumonDao bumonDao;
private IShainDao shainDao;
public void setup(Bumon bumon, Shain shain) {
// 部門の登録
bumonDao.insert(bumon);
// 社員の登録
shainDao.insert(shain);
}
public IBumonDao getBumonDao() {
return bumonDao;
}
public void setBumonDao(IBumonDao bumonDao) {
this.bumonDao = bumonDao;
}
public IShainDao getShainDao() {
return shainDao;
}
public void setShainDao(IShainDao shainDao) {
this.shainDao = shainDao;
}
}
最後に2つのDTOです。共にフィールドとそれに対するGetter/Setterを持つPOJOです。
Bumon.java
package dto;
public class Bumon {
private String cdBumon;
private String nmBumon;
public Bumon() {}
public Bumon(String cdBumon, String nmBumon) {
super();
this.cdBumon = cdBumon;
this.nmBumon = nmBumon;
}
public String getCdBumon() {
return cdBumon;
}
public void setCdBumon(String cdBumon) {
this.cdBumon = cdBumon;
}
public String getNmBumon() {
return nmBumon;
}
public void setNmBumon(String nmBumon) {
this.nmBumon = nmBumon;
}
}
Shain.java
package dto;
import java.util.Date;
public class Shain {
private String cdShain;
private String nmShimei;
private Date ymdNyuusha;
private String cdBumon;
public Shain() {}
public Shain(String cdShain, String nmShimei, Date ymdNyuusha,
String cdBumon) {
super();
this.cdShain = cdShain;
this.nmShimei = nmShimei;
this.ymdNyuusha = ymdNyuusha;
this.cdBumon = cdBumon;
}
public String getCdBumon() {
return cdBumon;
}
public void setCdBumon(String cdBumon) {
this.cdBumon = cdBumon;
}
public String getCdShain() {
return cdShain;
}
public void setCdShain(String cdShain) {
this.cdShain = cdShain;
}
public String getNmShimei() {
return nmShimei;
}
public void setNmShimei(String nmShamei) {
this.nmShimei = nmShamei;
}
public Date getYmdNyuusha() {
return ymdNyuusha;
}
public void setYmdNyuusha(Date ymdNyuusha) {
this.ymdNyuusha = ymdNyuusha;
}
}
まずSpring 2.0のBean定義ファイルで示した(空の)Bean定義ファイルを作成します。ファイル名は自由ですが、ここではapplicationContext.xmlとします。このファイルをプロジェクトのsrcフォルダ直下に作成します。後にプログラムからクラスパスリソースに含まれるBean定義ファイルを参照することになるため、srcフォルダにBean定義ファイルを置いておくと、Eclipseが自動的にクラスの出力フォルダへのコピーを行ってくれるため便利です。
尚、Eclipseのメニューからファイル>新規>その他>XML>XMLを選択し、「Create XML file from a DTD file」や「Create XML file from an XML schema file」を選ぶことにより、SpringのBean定義ファイルのDTD宣言やXMLスキーマ宣言が埋め込まれたXMLファイルを作成することも可能です。ただ、XML schemaの場合はスキーマロケーションを自分で埋める必要があるようですので、空のXMLファイルを作成して宣言をコピーした方が早いでしょう。
次に作成したBean定義ファイルをSpringプロジェクトに登録します。Springプロジェクトに登録することにより、Bean定義ファイルの検証や定義ファイルから参照しているJavaソースファイルの表示ができるようになります。
プロジェクトを右クリック>プロパティ>Springを選択し、AddボタンでBean定義ファイル(applicationContext.xml)を追加します(下図)。
作成したBean定義ファイルにBeanを登録します。まずはDAOから登録してみます。applicationContext.xmの<beans>の子要素として、以下を追加します。
<!-- DAO --> <bean id="bumonDao" class="dao.BumonDao"></bean> <bean id="shainDao" class="dao.ShainDao"></bean>
Spring 2.0でもBeanの登録には<bean>要素を使用し、必ず<beans>の子要素として記述します。id属性はBeanに付ける名前、class属性はBeanの実装クラス名です。
ここで要素名, 属性名, class属性の値はEclipseのコンテンツ・アシストで補完することができます。入力の手間と入力ミスを防ぐことができるため、Spring IDEを使う場合は積極的に使うと良いでしょう。また間違えて存在しないクラスの名前を与えた場合は、ソースコードのコンパイルエラーのようにエディタ左側に赤い×マークが付くので、ありがちな設定ファイルミスによるエラーを防ぐことができます。
次にServiceクラスを登録します。ServiceクラスではDAOへの依存関係を注入する必要があり、そのために<property>要素を使います。これもDAOと同じく、<beans>の子要素として追加して下さい。
<!-- Service -->
<bean id="bumonService" class="service.BumonService">
<property name="bumonDao" ref="bumonDao"/>
</bean>
<bean id="shainService" class="service.ShainService">
<property name="shainDao" ref="shainDao" />
</bean>
<bean id="setupService" class="service.SetupService">
<property name="shainDao" ref="shainDao" />
<property name="bumonDao" ref="bumonDao" />
</bean>
property要素のname属性に依存性を注入するためのsetterメソッドの名前(setXXXのXXXの部分で頭は小文字)、ref属性に注入対象となるBeanの名前を設定します。この例ではBumonServiceクラスがBumonDaoクラスを使用するため、BumonDaoクラスに付けたBeanの名前(bumonDao)をref属性の値としています。
このref属性の値もコンテンツ・アシストで補完・・・できるはずですが、Spring IDE 2.0M3では例外が発生してうまく動きませんでした。正式リリース時には改善されるのではないかと思います。
最後に作成したBean定義ファイルをグラフ表示して確認してみます。Beanのグラフ表示で示した図が表示されれば、Bean定義ファイルの作成はひとまず完了です。
今回は 開発環境の準備からBean定義ファイルの作成まで行いました。次回はSpring JDBCを使ってDAOを実装してみようと思います。
今回作成したソースファイルとapplicationContext.xmlはここからダウンロードできます。