You are on page 1of 31

SpringFramework 入門

DI 編
1章 開発環境の構築 ...................................................................................................................... 3
2章 SpringFramework とは ........................................................................................................... 5
3章 DI(Dependency Injection) ..................................................................................................... 5
4章 Spring プロジェクトの作成 ......................................................................................................... 6
5章 Spring 非対応プロジェクトのインポート ......................................................................................... 9
6章 ApplicationContext 経由でのコンポーネント取得 ....................................................................... 10
7章 設定ファイルを使ったセッターインジェクション ............................................................................... 14
8章 コンポーネントスキャン ............................................................................................................ 16
9章 @Autowired ........................................................................................................................ 19
10 章 MessageSource ................................................................................................................... 21
11 章 Web アプリで SpringFramework を利用する .............................................................................. 23
12 章 ユニットテスト ........................................................................................................................ 29
13 章 演習 .................................................................................................................................... 30

© 2017 Iterative corp. 2


1章 開発環境の構築
1.1 JDK のインストール
jdk-7u60-windows-i586.exe を実行し、JDK7 をインストールする。インストールの設定は特に変更しない。
また、環境変数 Path に JDK の bin フォルダ(C:¥Program Files¥Java¥jdk1.7.0_60¥bin)を先頭に追加する。

1.2 Spring Tool Suite のインストール


Spring Tool Suite(略して STS)は Spring に特化した機能を持つ Eclipse である。当研修では STS を利用する。
spring-tool-suite-3.5.1.RELEASE-e4.3.2-win32-installer.exe を実行する。
「Target Path」(インストール先)は c:¥springsource とする。
「Select Installation Packages」はデフォルト(全てチェックが入った状態)にする。
「JDK Path」は「C:¥Program Files¥Java¥jdk1.7.0_60」を指定する。

1.3 Spring Tool Suite の日本語化


STS を日本語化するために、preiades.zip を解凍して、c:¥springsource¥sts-3.5.1RELEASE に全て上書きする。
C:¥sts¥sts-3.5.1RELEASE¥sts.ini に下記を追加する。
(spring-tool-suite 日本語化 sts.ini 追記.txt をコピペーストする)
-javaagent:plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar
[スタート]-[Spring Tool Suite 3.5.1 RELEASE]を選択して STS を起動する。
ワークスペースは c:¥iterative¥spring とする

1.4 HSQLDB の説明


当研修では容易に DB 環境が構築できるように、HSQLDB を利用する。
rundb.bat を実行すると、HSQLDB のサーバーが起動する。(黒いコマンドプロンプトの画面を閉じると、サーバーが止
まることに注意)
また、runmanager.bat を実行すると、SQL 文を実行できる画面が起動する。

© 2017 Iterative corp. 3


1.5 Maven のリモートカタログの追加
Spring Tool Suite のメニューから[ウィンドウ]-[設定]を開き、左ペインの[Maven]-[アーキタイプ]を選択する。

「リモート・カタログの追加」を押下し、下記ダイアログの通り入力して OK ボタンを押下する。
カタログ・ファイル:http://repo1.maven.org/maven2/archetype-catalog.xml
説明:maven catalog

1.6 Maven のローカルリポジトリをコピー


当研修では Maven を利用してライブラリを取り込む。各自が都度インターネットにアクセスしてダウンロードすると、非
常に遅くなるため、予めローカルリポジトリにコピーを置いておく。
1 コマンドプロンプトを起動する。
2 以下のコマンドを実行する。
cd %USERPROFILE%
(ここでカレントディレクトリが「c:\Users\【Windows のユーザ名】
」になっていることを確認)
mkdir .m2
3 Explorer で c:¥【Windows のユーザ名】¥.m2 を開き、repository.zip 内の repository フォルダをコピーする。

© 2017 Iterative corp. 4


2章 SpringFramework とは
Rod Johnson 氏が著書「Expert One-on-One J2EE Development without EJB」で解説したフレームワークが起源。
当時 EJB が非常に重厚でなかなかメリットが享受できなかったことから、アンチ EJB として登場した。
SpringFramework の中心的な機能は DI、AOP(アスペクト志向プログラミング)である。

3章 DI(Dependency Injection)
3.1 DI とは
Dependency Injection とは、日本語で「依存性注入」という。依存とはあるクラスが別のクラスを利用していることを
言う。DI はこの依存関係を取り除き、モジュール間の結合を無くす(これを疎結合という)。別の言い方をすれば、DI は
システム全体で利用する各機能を紐づける機能である。

処理の呼び出し

呼び出すオブジェクト 呼び出されるオブジェクト

呼び出されるクラスを new して呼び出すオブジェクトに結びつけるのが DI

クラスを疎結合にすることにより、以下のメリットが享受できる。
⚫ テストがしやすくなる。
本番では製品用のコンポーネントを使い、テスト時には Mock 用のコンポーネントを使うことができるようになる。

⚫ 拡張しやすくなる
依存するプロダクトに手をいれずに、一部のコンポーネントをごっそり入れ替えることができる。
例えば、Web フレームワークとして Struts を使い、OR マッパーとして MyBatis を利用する場合、DI コンテナを使わな
ければ、製品コードが各コンポーネント(クラス)に直接依存することになる。直接依存すると、後から別のフレームワー
クに置き換える際に、大きな修正が必要になってしまう。DI を使えばこのような事態を避けることができる。

© 2017 Iterative corp. 5


4章 Spring プロジェクトの作成
4.1 プロジェクトの作成
[ファイル]-[新規]-[Maven プロジェクト]を選択する。

デフォルトのままで次へボタンを押下。

アーキタイプは、「org.apache.maven.archetypes」の「maven-archetype-quickstart」を選択。

© 2017 Iterative corp. 6


グループ ID に「jp.co.iterative」
アーティファクト ID に「spring-exam」
(パッケージは自動的に値が入力される)
を指定して、完了ボタンを押下する。

4.2 Maven の設定を追加する


pom.xml を開き、依存関係タブの追加ボタンを押下する。

下記依存関係を追加する。
グループ ID アーティファクト ID バージョン スコープ
junit junit 4.11 test
org.springframework spring-context 4.0.5.RELEASE compile
org.hsqldb hsqldb 2.2.8 compile
※junit については、バージョンの指定を変更すれば OK

© 2017 Iterative corp. 7


4.3 コンパイラの指定
プロジェクトで利用する Java コンパイラのバージョンを 1.6 に変更する。
pom.xml の「pom.xml タブ」を開き、</dependencies>の下に下記を追加する。
(内容は「maven_compiler_plugin 追記.txt」にあるのでコピーする。ただし、<build></build>は自分で記述す
る。)
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
</plugins>
</build>
追加したら、プロジェクト(spring-exam)を右クリックして、[Maven]-[プロジェクトの更新]をクリックする。

4.4 resources フォルダの追加


設定ファイルは src/main/resources に作成するのが慣習であるため、src/main を右クリックして、[新規]-[フォル
ダ]で「resources」を作成する。作成したら、プロジェクト(spring-exam)を右クリックして、[Maven]-[プロジェクトの
更新]をクリックする。
ここまで設定が完了すると、以下のようなフォルダ構成になる。

© 2017 Iterative corp. 8


5章 Spring 非対応プロジェクトのインポート
当研修では Spring に対応していないレガシーなプロジェクトを Spring に対応させていく形で学ぶ。Spring 非対応プロ
ジェクトは、「not-spring-exam.zip」として提供されている。これを Eclipse に取り込んで利用する。
Eclipse のメニューから[ファイル]- [インポート]を選択する。

「一般」の中の「既存プロジェクトをワークスペースへ」を選択して、「次へ」ボタンを押下する。

アーカイブ・ファイルの選択に not-spring-exam.zip を指定して、プロジェクトにチェックが入った状態で完了ボタンを


押下する。下記のように、パッケージエクスプローラに「not-spring-exam」が表示されれば OK。

© 2017 Iterative corp. 9


6章 ApplicationContext 経由でのコンポーネント取得
Spring の最も基本的な使い方は、XML ファイルに設定を記述し、ClassPathXmlApplicationContext を使ってイン
スタンスを生成する方法だ。

6.1 例題
BookFindRunner を修正し、Spring 経由で BookService のインスタンスを取得するように修正しなさい。

6.2 解答
まずは、下準備として以下のクラスをプロジェクト not-spring-exam からコピーする。
※パッケージは元のままになるようにコピーすること。例:BookDTO なら、jp.co.iterative.spring_exam.dto のまま
にすること。
BookDTO
BookService
MockBookService
BookFindRunner

Spring が生成したインスタンスを取得する処理
BookFindRunner で Factory を使って BookService を取得している個所を下記のとおり修正する。
BookService bookService = ServiceFactory.createBookService();

AbstractApplicationContext applicationContext =
new ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml");
BookService bookService = applicationContext.getBean(BookService.class);

ここで注目すべきは、インターフェースのみが記述されており、インスタンス生成したクラス名がどこにも記述されていな
い点である。クラス名の記述は、設定ファイルである META-INF/spring/applicationContext.xml で行う必要があ
る。
Bean 定義ファイル
「src/main/resources」を右クリックして、[新規]-[フォルダー]を選択する。フォルダー名には「META-INF/spring」
を指定する。
「src/main/resources/META-INF/spring」を右クリックして、[Spring Bean Configuration File]を選択する。

© 2017 Iterative corp. 10


ファイル名に「applicationContext.xml」を指定して「次へ」ボタンを押下する。

XSD の指定では beans と context にチェックを入れて「完了」ボタンを押下する。


beanタグを記述し、Springでインスタンスを生成するクラス
「jp.co.iterative.spring_exam.service.mock.MockBookService」を指定する。
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">

<bean class="jp.co.iterative.spring_exam.service.mock.MockBookService"/>

</beans>

© 2017 Iterative corp. 11


実行する
BookFindRunner を右クリックし、[実行]-[Java アプリケーション]を選択して実行する。
書籍 ID を入力してください。

7 15, 2014 8:12:35 午後 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh


情報: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@158291: startup date [Tue Jul 15
20:12:35 JST 2014]; root of context hierarchy
7 15, 2014 8:12:35 午後 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
情報: Loading XML bean definitions from class path resource [META-INF/spring/applicationContext.xml]
書籍名は永遠の1です。
7 15, 2014 8:12:36 午後 org.springframework.context.support.ClassPathXmlApplicationContext doClose
情 報 : Closing org.springframework.context.support.ClassPathXmlApplicationContext@158291: startup date [Tue Jul 15
20:12:35 JST 2014]; root of context hierarchy
以上で MockBookService が動作していることがわかる。なお、MockBookService は「永遠の1」という書籍を必ず戻
す処理になっている。

6.3 解説
Factory クラスを使ってインスタンスを生成する場合、以下のように Factory を介して Runner と各 Service クラスに依
存が生じている。

Spring を利用することで、以下のように Runner と各 Service クラスの依存関係が解消する。

型を指定した Bean の取得


ここまでの話で、Bean の取得方法として ApplicationContext#getBean(Class cls)の引数にインタフェースの型を指
定すればよいことがわかった。
この場合の注意点として、引数で指定した型の Bean が、Spring 上に複数定義されていると、例外が発生する。
例えば、MockBookService と実装が全く同じ MockBookService2 を登録したとする。
<bean class="jp.co.iterative.spring_exam.service.mock.MockBookService"/>
<bean class="jp.co.iterative.spring_exam.service.mock.MockBookService2"/>

この状態で、BookFindRunner を実行すると、例外が発生する。

© 2017 Iterative corp. 12


BookService bookService = applicationContext.getBean(BookService.class);

例外のスタックトレース
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of
type [jp.co.iterative.spring_exam.service.BookService] is defined: expected single matching bean but found 2:
jp.co.iterative.spring_exam.service.mock.MockBookService#0,jp.co.iterative.spring_exam.service.mock.MockBookServic
e#1
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:313)
at
org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:985)
at jp.co.iterative.spring_exam.runner.BookFindRunner.main(BookFindRunner.java:19)
例外のメッセージからわかるように、型を指定して Bean を要求されても、特定できないため例外となる。

id を指定した Bean の取得


Bean に付与された id をもとに、処理から Bean を取得することができる。
例えば、下記のように id 属性を指定して名前を付与する。
<bean id="mock1" class="jp.co.iterative.spring_exam.service.mock.MockBookService"/>
<bean id="mock2" class="jp.co.iterative.spring_exam.service.mock.MockBookService2"/>

処理からは getBean の引数で id を指定することにより、Bean を取得することができる。


BookService bookService = (BookService)applicationContext.getBean("mock1");

© 2017 Iterative corp. 13


7章 設定ファイルを使ったセッターインジェクション
7.1 例題
BookService のコンポーネントを MockBookService から BookServiceImpl に切り替えなさい。また、BookDAO は
Spring のコンポーネントとして取得して呼び出しなさい。

7.2 解答
まずは、下準備として以下のクラスをプロジェクト not-spring-exam からコピーする。※パッケージは元のままになる
ようにコピーすること。
BookServiceImpl
BookDAO
MockBookDAO

BookServiceImpl を下記のとおり変更する。
package jp.co.iterative.spring_exam.service;

import jp.co.iterative.spring_exam.dao.BookDAO;
import jp.co.iterative.spring_exam.dto.BookDTO;

public class BookServiceImpl implements BookService{


private BookDAO bookDAO;

@Override
public BookDTO findByBookId(String bookId) {
// BookDAO bookDAO = DAOFactory.createBookDAO();
return bookDAO.selectByBookId(bookId);
}
@Override
public int addBooks(List<BookDTO> books) {
return 0;
}
public void setBookDAO(BookDAO bookDAO) {
this.bookDAO = bookDAO;
}
}

applicationContext.xml を下記のとおり変更する。
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">

<bean id="bDAO" class="jp.co.iterative.spring_exam.dao.mock.MockBookDAO"/>

<bean class="jp.co.iterative.spring_exam.service.BookServiceImpl">
<property name="bookDAO" ref="bDAO"/>

© 2017 Iterative corp. 14


</bean>

</beans>

7.3 解説
インジェクション
Spring で生成したコンポーネントに、別のコンポーネントを、インジェクション(注入)することができる。
Spring は以下の種類のインジェクションが用意されている。
⚫ セッターインジェクション
⚫ コンストラクタインジェクション
どちらもインジェクションできる点では同じだが、セッターインジェクションが推奨されている。

セッターインジェクション
セッターインジェクションはセッター経由でコンポーネントをインジェクション(注入)する方式。
例題のように注入される側のクラスにインスタンス変数とそれに対するセッターを用意する。また、Bean 定義ファイルで
は property タグの属性でプロパティ名(name 属性)と注入するコンポーネント(ref 属性)を指定する。

コンストラクタインジェクションはコンストラクタの引数経由でコンポーネントをインジェクションする。

BookServiceImpl
public class BookServiceImpl implements BookService{
private BookDAO bookDAO;

public BookServiceImpl(BookDAO bookDAO) {


this.bookDAO = bookDAO;
}

@Override
public BookDTO findByBookId(String bookId) {
// BookDAO bookDAO = DAOFactory.createBookDAO();
return bookDAO.selectByBookId(bookId);
}

applicationContext.xml
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">

<bean id="bDAO" class="jp.co.iterative.spring_exam.dao.mock.MockBookDAO"/>

<bean class="jp.co.iterative.spring_exam.service.BookServiceImpl">
<constructor-arg index="0" ref="bDAO"/>
</bean>

© 2017 Iterative corp. 15


</beans>

7.4 演習
演習7-① BookDAO に イ ン ジ ェ ク シ ョ ン す る コ ン ポ ー ネ ン ト の ク ラ ス を BookDAOImpl に し て 、
BookFindRunner を完成させなさい。

8章 コンポーネントスキャン
設定ファイルでコンポーネントをひとつひとつ記述するのは非常に面倒である。特に bean タグの class 属性に FQCN
を記述するなど、TYPO しやすい方式となっている。
アノテーションによる設定を利用することで、設定ファイルを記述する手間を省け、容易にコンポーネント化することがで
きる。

8.1 例題
コンポーネントスキャンにより、MockPublisherService をコンポーネント化しなさい。

8.2 解答
まずは、下準備として以下のクラスをプロジェクト not-spring-exam からコピーする。
PublisherDTO
PublisherService
MockPublisherService
PublisherFindRunner

PublisherFindRunner で PublisherService を取得している処理を ApplicationContext 経由で取得するように変


更する。
// PublisherService publisherService = ServiceFactory.createPublisherService();
AbstractApplicationContext applicationContext
= new ClassPathXmlApplicationContext ("META-INF/spring/applicationContext.xml");
PublisherService publisherService = applicationContext.getBean(PublisherService.class);

この時点では、PubliserService のコンポーネントが登録されていないため、例外が発生する。

component-scan の設定
applicationContext.xml に下記を追加する。
<context:component-scan base-package="jp.co.iterative"/>

© 2017 Iterative corp. 16


コンポーネントの登録
MockPublisherService のクラス宣言に@Component を追加する。
@Component
public class MockPublisherService implements PublisherService{

8.3 解説
コンポーネントスキャンはスキャン対象のパッケージ配下のクラスを走査し、@Component などのアノテーションが付
加されている場合に、コンポーネントとして処理する。スキャン対象のパッケージは、 component-scan タグの
base-package 属性で指定する。

では、PublisherServiceImpl もコンポーネント登録してみよう。PublisherServiceImpl を no-spring-exam からコ


ピーして、findByPublisherId の実装のコンパイルエラーが無くすために、下記実装にする。
@Override
public PublisherDTO findByPublisherId(String publisherId) {
// PublisherDAO publisherDAO = DAOFactory.createPublisherDAO();
// return publisherDAO.selectByPublisherId(publisherId);
return new PublisherDTO();
}

また、コンポーネント登録するために、クラス宣言に@Component を付加する。
@Component
public class PublisherServiceImpl implements PublisherService{

この状態で PublisherFindRunner を実行すると例外が発生する。なぜなら、PublisherService に該当するコンポー


ネントが複数登録されているためである。

この問題を回避するには、コンポーネントスキャンで PublisherService のコンポーネントをひとつだけ登録するように


すればよい。そのためには、コンポーネントスキャンのフィルタの仕組みを理解する必要がある。
コンポーネントスキャンのフィルタ
コンポーネントスキャンにはインクルードフィルタ(<include-filter>)とエクスクルードフィルタ(<exclude-filter>)の
組み合わせで対象のコンポーネントを指定できるようになっている。

© 2017 Iterative corp. 17


インクルードフィルタ クラス A クラス B クラス C クラス D

①インクルードフィルタでコンポーネント化対象のクラスが決定される。

エクスクルードフィルタ クラス A クラス B クラス C クラス D

②エクスクルードフィルタでコンポーネント化対象のクラスから除外される。

インクルードフィルタもエクスクルードフィルタもそれぞれ type 属性でフィルタの種類を指定し、expression 属性で指定


内容を記述する。フィルタの type には以下の種類が存在する。
type 属性 expression 属性に 説明
記載する内容
annotation アノテーションの FQCN 指定されたアノテーションを対象とする。
assignable クラスやインタフェースの 指定された型自体か、継承関係にあるクラスを対象とする。
FQCN
aspectj AspectJ 記法 AspectJ の型表記にマッチするクラスを対象とする。
regex 正規表現 正規表現にマッチするクラスを対象とする。
custom フィルタの FQCN 自作のフィルタを指定し、その処理で対象のクラスを決定する。
include-filter に annotation 以外を指定した場合、@Component が付いていないクラスも、条件に合致すればコン
ポーネント対象となる点に注意が必要だ。例えば、type が regex のインクルードフィルタは、@Component を付加し
ていないクラスでも、正規表現にマッチすればコンポーネント対象とする。
なお、デフォルトでは、include-filter の type="annotation"で、Component 等が指定されている。もし、デフォルト
のフィルタを適用したくない場合は、component-scan タグの use-default-filters 属性に false を指定する。

今回は、component-scan タグのボディで exclude-filter を指定し、スキャン対象を除外する。


例えば、Mock のクラスを無効にするには、下記のように指定する。
<context:component-scan base-package="jp.co.iterative">
<context:exclude-filter type="regex" expression="^.*Mock.*Service$"/>
</context:component-scan>
逆に製品のクラス(~ServiceImpl)を無効にするには、下記のように指定する。
<context:component-scan base-package="jp.co.iterative">
<context:exclude-filter type="regex" expression="^.*ServiceImpl$"/>
</context:component-scan>

© 2017 Iterative corp. 18


9章 @Autowired
9.1 例題
PublisherServiceImpl に MockPublisherDAO をインジェクションしなさい。

9.2 解答
まずは、下準備として以下のクラスをプロジェクト not-spring-exam からコピーする。
PublisherDAO
MockPublisherDAO

PublisherServiceImpl を下記のように修正する。
@Component
public class PublisherServiceImpl implements PublisherService{
@Autowired
private PublisherDAO publisherDAO;

@Override
public PublisherDTO findByPublisherId(String publisherId) {
return publisherDAO.selectByPublisherId(publisherId);
}
}
※セッターは不要

また、MockPublisherDAO のクラス宣言に@Conponent を付加する。


@Component
public class MockPublisherDAO implements PublisherDAO{

9.3 解説
コンポーネントのプロパティに@Autowired を付加するだけで、インジェクションさせることができる。ここで気を付けて
ほしいのが、Spring のコンポーネントとして動作している場合に、@Autowired が使えるという点だ。つまり、下図のよ
うに FooRunner は FooService をコンポーネントとして取得しているので、FooServiceImpl の@Autowired が機能し
て DI される。一方、BarRunner は単なるクラスなのでここに@Autowired を記述しても DI されない。

9.4 設定ファイル vs アノテーション


SpringFramework の DI の設定は、設定ファイル(XML)による記述と、アノテーションによる設定の 2 種類が存在す
るわけだが、それぞれ一長一短ある。設定ファイルによる設定は、全てを 1 か所にまとめて管理できるメリットがあるが、
実装時はソースコードと設定ファイルの両方を変更することになり面倒である。アノテーションはその逆で、ソースコード
に設定を直接記述できる分、コーディングしやすいが、設定が色々な場所に書かれるため、集中管理しにくい。

© 2017 Iterative corp. 19


また、アノテーションを付加できるのは、自分で書いたコードのみであるため、サードパーティやオープンソースで提供さ
れるクラスをコンポーネントとして DI するには、設定ファイルで行う必要がある。

© 2017 Iterative corp. 20


10章 MessageSource
10.1 MessageSource とは
画面への表示文言等はプログラム内に直接記述することは望ましくない。それは、下記理由のためである。
⚫ 同じ文言が色々なプログラムに冗長に記載され、メンテナンス性が著しく低下する。
⚫ アプリケーションを多言語に対応する必要が出た際に困難になる。
Java のアプリケーションではプロパティファイルに文言を記載することが推奨されている。SpringFramework にはこの
プロパティファイルを読み込み、メッセージを取得する仕組みが提供されている。

10.2 STS にプロパティエディタをインストールする


STS 標準でプロパティファイルに日本語を入力すると、文字化けするのでプロパティエディタをインストールする。
jp.gr.java_conf.ussiy.app.propedit_6.0.3.zip の 中 身 ( features フ ォ ル ダ お よ び plugins フ ォ ル ダ ) を 、
C:¥sts¥sts-3.5.1.RELEASE に全てコピーする。

10.3 例題
BookFindRunner 実行時に存在しない書籍 ID を指定した場合、「条件に一致するデータが存在しません」のメッセー
ジを表示しなさい。なお、メッセージはメッセージソースから取得するものとする。

10.4 解答
applicationContext.xml に下記を追加する。
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="META-INF/message"/>
</bean>

/src/main/resources/META-INF/message.properties に下記を記述する。
not_found=条件に一致するデータがありません。

BookFindRunner でメッセージを取得する処理を記述する。
System.out.println(applicationContext.getMessage("not_found", null, Locale.getDefault()));

10.5 演習
演習10-① 業種テーブル(BUSINESS_TYPE)の業種名に対して部分一致検索をした結果を表示するプログラムを
作成しなさい。また、以下の場合はそれぞれ該当のエラーメッセージを表示する。
➢ 1 件もヒットしなかった場合は「条件に一致するデータがありません。」
➢ 検索条件に何も入力されなかった場合は「条件を入力してください。」

© 2017 Iterative corp. 21


実行例
業種名の一部分を入力してください。
> 業
BT012:製造業
BT013:小売業
BT014:サービス業

© 2017 Iterative corp. 22


11章 Web アプリで SpringFramework を利用する
SpringFramework を Servlet で利用することを考えよう。最も容易に考えられる方法は、ここまで扱ってきた
ClassPathXmlApplicationContext を Servlet から利用する方法だ。しかし、この方法は利用してはならない。なぜな
ら、Servlet を呼び出すたびに、Spring が設定ファイルを読み込んで、それぞれが異なるコンポーネントを生成すること
になるためである。
つまり、SpringFramework のコンテキストは Web アプリケーション全体で 1 つにしなければならない。
それでは、実際に SpringFramework を利用した Web アプリケーションを作っていこう。

11.1 Web アプリケーションを作成する。


[ファイル]-[新規]-[Maven プロジェクト]を選択する。
アーキタイプの選択で「maven-archetype-webapp」を選択する。

グループ ID に「jp.co.iterative」
アーティファクト ID に「spring-web-exam」
を指定し、完了ボタンを押下する。

11.2 プロジェクトの設定
Spring-exam プロジェクト作成と同様に、pom.xml の「pom.xml タブ」を開き、</dependencies>の下にある

© 2017 Iterative corp. 23


<build></build>の中の<finalName>の直下に「maven_compiler_plugin 追記.txt」の内容をコピーする。

Web アプリケーションに必要なライブラリを追加していく。pom.xml の依存関係タブを選択する。

追加ボタンを押下する。
下記依存関係を追加する。
グループ ID アーティファクト ID バージョン スコープ
junit junit 4.11 test
org.springframework spring-context 4.0.5.RELEASE compile
org.springframework spring-webmvc 4.0.5.RELEASE compile
javax.servlet servlet-api 2.4 provided
org.hsqldb hsqldb 2.2.8 compile
※junit については、バージョンの指定を変更すれば OK
追加したら、プロジェクト(spring-exam)を右クリックして、[Maven]-[プロジェクトの更新]をクリックする。
ここまでで、WEB アプリケーションとしてセットアップが完了。

11.3 tc Server にアプリケーションをデプロイする

サーバービューの「VMware vFabric tc Server Developer Edition v2.9」を右クリックし、「追加および除去」をクリッ


クする。

© 2017 Iterative corp. 24


「追加および除去」で使用可能欄にある「spring-web-exam」を選択し、追加ボタンを押下する。下図のように構成済
み欄に移動した状態で、完了ボタンを押下する。

tc Servet を起動し、ブラウザから下記 URL にアクセスする。


http://localhost:8080/spring-web-exam/
画面に「Hello World!」と表示されれば正常。

11.4 検証用の Servlet を作成する


tc Server をシャットダウンして、フォルダ「src/main/java」を作成する。
サーブレットクラス(jp.co.iterative.spring_web_exam.servlet.ScopeSampleServlet)を作成する。
public class ScopeSampleServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("ScopeSampleServlet");
}
}

web.xml に下記を追加する。
<servlet>
<servlet-name>ScopeSampleServlet</servlet-name>
<servlet-class>jp.co.iterative.spring_web_exam.servlet.ScopeSampleServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ScopeSampleServlet</servlet-name>
<url-pattern>/scopeSample</url-pattern>
</servlet-mapping>

tc サーバーを起動後、下記 URL にアクセスして、コンソールに「ScopeSampleServlet」と表示されることを確認する。


http://localhost:8080/spring-web-exam/scopeSample

11.5 検証用のコンポーネントを作成する
Web アプリケーションを利用する際に気をつけなければならないのは、Servlet コンテナが1プロセスで動作し続ける点
と、Servlet がマルチスレッドで動作する点だ。インスタンス生成は少なければ少ないほど、システム全体のパフォーマ

© 2017 Iterative corp. 25


ンスが上がるため、複数のスレッドから同一のインスタンスを使いまわすことが望ましい。一方、スレッドセーフではない
クラスを複数のスレッドで共有すると、不具合を引き起こしてしまうため、インスタンスの使いまわしを適切に行う必要が
ある。これを実際に体験してみよう。

インタフェース(jp.co.iterative.spring_web_exam.service.ScopeSampleService)を作成する。
public interface ScopeSampleService {
public Date getCurrentTime();
}

コンポーネント(jp.co.iterative.spring_web_exam.service.ScopeSampleServiceImpl)を作成する。
@Component
public class ScopeSampleServiceImpl implements ScopeSampleService{

private Date currentTime;

public ScopeSampleServiceImpl() {
currentTime = new Date();
}
@Override
public Date getCurrentTime() {
return currentTime;
}
}

ScopeSampleServlet からコンポーネントを取得して呼び出す。
public class ScopeSampleServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("ScopeSampleServlet");
WebApplicationContext wac = WebApplicationContextUtils
.getRequiredWebApplicationContext(getServletContext());
ScopeSampleService sampleService = wac.getBean(ScopeSampleService.class);

Date currentTime = sampleService.getCurrentTime();

PrintWriter printWriter = resp.getWriter();


printWriter.write(sampleService + ":" + currentTime.toString());

}
}

この時点では、コンポーネントスキャンの設定がどこにもないので、NullPointerException が発生する。

© 2017 Iterative corp. 26


11.6 Bean 定義を設定する
「src/main/resources」を右クリックして、[新規]-[フォルダー]を選択する。フォルダー名には「META-INF/spring」
を指定する。
「src/main/resources/META-INF/spring」を右クリックして、[Spring Bean Configuration File]を選択する。
ファイル名に「applicationContext.xml」を指定して「次へ」ボタンを押下する。
XSD の指定では beans と context にチェックを入れて「完了」ボタンを押下する。

src/main/resources/META-INF/spring/applicationContext.xml を下記のとおり作成する。
<context:annotation-config/>
<context:component-scan base-package="jp.co.iterative.spring_web_exam.service">
</context:component-scan>

web.xml に下記を追加する。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:META-INF/spring/applicationContext.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

再度 Servlet を実行すると、下記のような画面が表示される。

異なるブラウザで呼び出しても、同じ日時が表示される。これは、アプリケーション全体で ScopeSampleServiceImpl
のインスタンスが 1 つ生成されているためである。
次に ScopeSampleServiceImpl を下記のように変更してから実行してみよう。
@Component
@Scope("prototype")
public class ScopeSampleServiceImpl implements ScopeSampleService{

今度は、実行する度に表示が変わる。

© 2017 Iterative corp. 27


11.7 コンポーネントのスコープ
Spring が生成するコンポーネントはスコープを定義できる。スコープとは Spring がどのような単位でインスタンス生成
するかを制御するものである。SpringFramework ではクラス定義に@Scope を付加することで、スコープを指定でき
る。スコープ種類は以下の 4 つである。
スコープ 説明
singleton シングルトンのインスタンスとする。つまり、JVM 全体で 1 インスタンスが使いまわされることとな
る。デフォルトが singleton なので、Scope を指定しない場合は、この設定が有効となる。
session ServletAPI の session スコープの間、同じインスタンスが使いまわされる。
request ServletAPI の request スコープの間、同じインスタンスが使いまわされる。
prototype 毎回新しいインスタンスを生成する。

なお、session と request については、Web アプリケーションのみで利用でき、ServletAPI と連動するように Listener


を追加する必要がある。
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

もし、コンポーネントにインスタンス変数を持たせ、マルチスレッド環境で動作させる場合には、singleton にしてしまうと
データが壊れるため、注意する必要がある。

© 2017 Iterative corp. 28


12章 ユニットテスト
Spring の機能を使ったコンポーネントをテストするには、 Spring 独自のテスト機能を利用する。テスト機能は
spring-test で提供されているため、pom.xml に依存関係を追加する必要がある。
プロジェクト spring-exam の pom.xml に下記依存関係を追加する。

12.1 テストクラスの作成
では、早速テストクラスを作成しよう。
BookServiceTest
package jp.co.iterative.spring_exam.service;

import static org.hamcrest.CoreMatchers.*;


import static org.junit.Assert.*;

import jp.co.iterative.spring_exam.dto.BookDTO;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:META-INF/spring/applicationContext.xml")
public class BookServiceTest {

@Autowired
private BookService bookService;

@Test
public void test() {
BookDTO bookDTO = bookService.findByBookId("B0000003");
assertThat(bookDTO.getBookName(), is("真珠のプログラム"));
}

12.2 解説
SpringJUnit4ClassRunner を利用すれば、テストで利用するコンポーネントを@Autowire で DI できるため、容易にテ
ストを実行できる。

© 2017 Iterative corp. 29


13章 演習
プレゼンテーション層(Servlet)とビジネス層(Service)+データアクセス層(DAO)を同時に実装することを想定して、
プログラムを作成しよう。カテゴリ テーブルに対して検索して結果を表示する CategoryDetailServlet(URL は
/categoryDetail)を作成する。
例えば、URL に
http://localhost:8080/categoryDetail?categoryId=C003
を入力したとき、下記画面を表示する。

実装の流れ

13.1 準備およびプレゼンテーション層
準備
まずは、CategoryDTO クラスを作成し、CategoryService インタフェースを作成する。DTO は全レイヤをまたぐクラス
であり、Service はプレゼンテーション層とビジネス層間のインタフェースだからである。
プレゼンテーション層
プレゼンテーション層を作るためには、Service に対する Mock クラスが必要となる。MockCategoryService クラスを
作成して固定値を戻すようにする。また、Bean 定義ファイルからこの Mock クラスを読み込むように修正する。
この状態で CategoryDetailServlet や categoryDetail.jsp を作成する。
下記 URL にアクセスして、Mock クラスで設定した文言が出力されることを確認する。
http://localhost:8080/spring-web-exam/categoryDetail

13.2 ビジネス層+データアクセス層
DAO のインタフェース(CategoryDAO)を作成し、それに対するテストクラスを作成する。また、CategoryDAOImpl を
作成して Bean 定義ファイルから読み込まれるようにする。
テストクラスの作成
テストクラスを格納するフォルダ(src/test/java)を作成し、そこにテストクラス CategoryDaoTest を作成する。そして、
CategoryDAOImpl を完成させる。
また、CategoryServiceImpl を作成し、それに対するテストクラスを作成する。

13.3 結合
プレゼンテーション層とデータアクセス層を結合する。具体的には Bean 定義ファイルでコンポーネント化するクラスを
MockCategoryService から CategoryServiceImpl に切り替え、ブラウザから下記 URL にアクセスする。
http://localhost:8080/spring-web-exam/categoryDetail?categoryId=C001

© 2017 Iterative corp. 30


発行者

株式会社イテレイティブ
東京都千代田区神田神保町1-7日本文芸社ビル 6 階

本書の一部または全部を株式会社イテレイティブから正式な許諾を得ずにいかなる方法
(転載、転用、送信、上映等)においても無断で複写、複製することは禁止されています。

© 2017 Iterative corp. 31

You might also like