Professional Documents
Culture Documents
01 Spring入門 (DI編)
01 Spring入門 (DI編)
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
「リモート・カタログの追加」を押下し、下記ダイアログの通り入力して OK ボタンを押下する。
カタログ・ファイル:http://repo1.maven.org/maven2/archetype-catalog.xml
説明:maven catalog
3章 DI(Dependency Injection)
3.1 DI とは
Dependency Injection とは、日本語で「依存性注入」という。依存とはあるクラスが別のクラスを利用していることを
言う。DI はこの依存関係を取り除き、モジュール間の結合を無くす(これを疎結合という)。別の言い方をすれば、DI は
システム全体で利用する各機能を紐づける機能である。
処理の呼び出し
呼び出すオブジェクト 呼び出されるオブジェクト
クラスを疎結合にすることにより、以下のメリットが享受できる。
⚫ テストがしやすくなる。
本番では製品用のコンポーネントを使い、テスト時には Mock 用のコンポーネントを使うことができるようになる。
⚫ 拡張しやすくなる
依存するプロダクトに手をいれずに、一部のコンポーネントをごっそり入れ替えることができる。
例えば、Web フレームワークとして Struts を使い、OR マッパーとして MyBatis を利用する場合、DI コンテナを使わな
ければ、製品コードが各コンポーネント(クラス)に直接依存することになる。直接依存すると、後から別のフレームワー
クに置き換える際に、大きな修正が必要になってしまう。DI を使えばこのような事態を避けることができる。
デフォルトのままで次へボタンを押下。
アーキタイプは、「org.apache.maven.archetypes」の「maven-archetype-quickstart」を選択。
下記依存関係を追加する。
グループ 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
「一般」の中の「既存プロジェクトをワークスペースへ」を選択して、「次へ」ボタンを押下する。
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]を選択する。
<bean class="jp.co.iterative.spring_exam.service.mock.MockBookService"/>
</beans>
6.3 解説
Factory クラスを使ってインスタンスを生成する場合、以下のように Factory を介して Runner と各 Service クラスに依
存が生じている。
この状態で、BookFindRunner を実行すると、例外が発生する。
例外のスタックトレース
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 を要求されても、特定できないため例外となる。
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;
@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 class="jp.co.iterative.spring_exam.service.BookServiceImpl">
<property name="bookDAO" ref="bDAO"/>
</beans>
7.3 解説
インジェクション
Spring で生成したコンポーネントに、別のコンポーネントを、インジェクション(注入)することができる。
Spring は以下の種類のインジェクションが用意されている。
⚫ セッターインジェクション
⚫ コンストラクタインジェクション
どちらもインジェクションできる点では同じだが、セッターインジェクションが推奨されている。
セッターインジェクション
セッターインジェクションはセッター経由でコンポーネントをインジェクション(注入)する方式。
例題のように注入される側のクラスにインスタンス変数とそれに対するセッターを用意する。また、Bean 定義ファイルで
は property タグの属性でプロパティ名(name 属性)と注入するコンポーネント(ref 属性)を指定する。
コンストラクタインジェクションはコンストラクタの引数経由でコンポーネントをインジェクションする。
BookServiceImpl
public class BookServiceImpl implements BookService{
private 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 class="jp.co.iterative.spring_exam.service.BookServiceImpl">
<constructor-arg index="0" ref="bDAO"/>
</bean>
7.4 演習
演習7-① BookDAO に イ ン ジ ェ ク シ ョ ン す る コ ン ポ ー ネ ン ト の ク ラ ス を BookDAOImpl に し て 、
BookFindRunner を完成させなさい。
8章 コンポーネントスキャン
設定ファイルでコンポーネントをひとつひとつ記述するのは非常に面倒である。特に bean タグの class 属性に FQCN
を記述するなど、TYPO しやすい方式となっている。
アノテーションによる設定を利用することで、設定ファイルを記述する手間を省け、容易にコンポーネント化することがで
きる。
8.1 例題
コンポーネントスキャンにより、MockPublisherService をコンポーネント化しなさい。
8.2 解答
まずは、下準備として以下のクラスをプロジェクト not-spring-exam からコピーする。
PublisherDTO
PublisherService
MockPublisherService
PublisherFindRunner
この時点では、PubliserService のコンポーネントが登録されていないため、例外が発生する。
component-scan の設定
applicationContext.xml に下記を追加する。
<context:component-scan base-package="jp.co.iterative"/>
8.3 解説
コンポーネントスキャンはスキャン対象のパッケージ配下のクラスを走査し、@Component などのアノテーションが付
加されている場合に、コンポーネントとして処理する。スキャン対象のパッケージは、 component-scan タグの
base-package 属性で指定する。
また、コンポーネント登録するために、クラス宣言に@Component を付加する。
@Component
public class PublisherServiceImpl implements PublisherService{
①インクルードフィルタでコンポーネント化対象のクラスが決定される。
②エクスクルードフィルタでコンポーネント化対象のクラスから除外される。
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);
}
}
※セッターは不要
9.3 解説
コンポーネントのプロパティに@Autowired を付加するだけで、インジェクションさせることができる。ここで気を付けて
ほしいのが、Spring のコンポーネントとして動作している場合に、@Autowired が使えるという点だ。つまり、下図のよ
うに FooRunner は FooService をコンポーネントとして取得しているので、FooServiceImpl の@Autowired が機能し
て DI される。一方、BarRunner は単なるクラスなのでここに@Autowired を記述しても DI されない。
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 件もヒットしなかった場合は「条件に一致するデータがありません。」
➢ 検索条件に何も入力されなかった場合は「条件を入力してください。」
グループ ID に「jp.co.iterative」
アーティファクト ID に「spring-web-exam」
を指定し、完了ボタンを押下する。
11.2 プロジェクトの設定
Spring-exam プロジェクト作成と同様に、pom.xml の「pom.xml タブ」を開き、</dependencies>の下にある
追加ボタンを押下する。
下記依存関係を追加する。
グループ 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 アプリケーションとしてセットアップが完了。
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>
11.5 検証用のコンポーネントを作成する
Web アプリケーションを利用する際に気をつけなければならないのは、Servlet コンテナが1プロセスで動作し続ける点
と、Servlet がマルチスレッドで動作する点だ。インスタンス生成は少なければ少ないほど、システム全体のパフォーマ
インタフェース(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{
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);
}
}
この時点では、コンポーネントスキャンの設定がどこにもないので、NullPointerException が発生する。
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{
今度は、実行する度に表示が変わる。
もし、コンポーネントにインスタンス変数を持たせ、マルチスレッド環境で動作させる場合には、singleton にしてしまうと
データが壊れるため、注意する必要がある。
12.1 テストクラスの作成
では、早速テストクラスを作成しよう。
BookServiceTest
package jp.co.iterative.spring_exam.service;
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 できるため、容易にテ
ストを実行できる。
実装の流れ
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
株式会社イテレイティブ
東京都千代田区神田神保町1-7日本文芸社ビル 6 階
本書の一部または全部を株式会社イテレイティブから正式な許諾を得ずにいかなる方法
(転載、転用、送信、上映等)においても無断で複写、複製することは禁止されています。