リソースフレームワーク チュートリアル
0. はじめに
hifive リソースフレームワークは、データやファイルをサーバ上に保存する
REST APIを実装することを目的とした、Java用のフレームワークです。
これを使うことで、URLの形で表されたファイルやデータを操作する、
いわゆるREST APIを簡単に実装できるようになります。
hifiveにおけるサーバ用のフレームワークは大きく2階層に分かれています。
- リソースフレームワーク
- URL上にマップされたリソースに対して操作を行う、REST APIを提供するための
フレームワーク - Syncフレームワーク
- 上記フレームワークをベースに、クライアント-サーバ間や複数のクライアント間で
データを同期するための仕組みを持った実装を作成するためのフレームワーク
このチュートリアルでは、上の「リソースフレームワーク」の利用方法を学びます。
0.1 目的と概念
チュートリアルを始める前に、簡単にこのフレームワークの目的と
そのための概念を説明しておきます。
いわゆる「REST」では、「名前の付いた情報」のことを「リソース」と呼び、識別子としてそれぞれURLがふられています。
REST APIはそれらの各URLに対してHTTPのGET, PUTなどメソッドを通しての操作を行うというスタイルのAPIになっています。
たとえば、あるサーバ上に以下のようなURLがあるとしましょう。
http://localhost:8080/hifive-resource-sample/resources/person/1
このサーバは例えばユーザ管理のREST APIで、このURLに対してGETリクエストを
送ることで「person」のID「1」のデータを、以下のようなJSON形式で
取得することができるとします。
"personId":"1",
"name":"hi five",
"age":"20",
"organization":"hifive developer team"
}
また、このサーバ上には他にも多数のpersonの情報が保管されており、
例えば、~/person/2には別のpersonの情報が入っている、とします。
このような状態をJava風に言うならば、先ほどのURLに対してのリクエストは
「person」型の、ID「1」を持つインスタンス(の情報)を取得する、ということになります。
このフレームワークでは
「型」の部分(ここでは「person」)を、「リソースクラス」、
「インスタンス」の部分(ここでは「person/1」)を「リソースアイテム」
と呼びます。
また、同じURLに対して、PUTのリクエストを送ることで、値を書き換えたり、
存在しない場合は、その場にデータを置くこともできます。
このような「操作」のことを、このフレームワークにおいては「アクション」と呼びます。
このフレームワークは、
「リソースクラス」という形を持ち、URL空間上に配置された「リソースアイテム」に対して
定義された「アクション」を実行することができるフレームワーク
です。
また、各リソースアイテムにはIDが必ず必要です。これを「リソースアイテムID」と呼びます。
先ほどのURLを模式的に書くと、
http://localhost:8080/hifive-resource-sample/resources/[リソース名]/[リソースアイテムID]
ということになります。
このように、URL上にデータがマッピングされており、それに対して操作ができることで、
WebブラウザやXMLHttpRequestから(もちろんh5.ajaxからも)、サーバ上に置かれたデータを
読み出したり書き込んだりすることが可能になります。
このフレームワークの目的は、このようなことができる環境を簡単に実装できるようにすることです。
例えば、住所録アプリの場合、各リソースアイテムを1人1人のエントリとし、
それらに対して、REST APIを発行するようなクライアントを書いてあげれば、
サーバ上に住所録を保存することができます。
もちろん、このフレームワークは認証機構も提供していますので、指定したユーザ以外の人は
データを見ることはできません。
以降の説明では、この章の用語を元に説明をしていきます。
この概念はぜひ押さえておいてください。
0.2 基本機能
このフレームワークは、大きく
- リソースのインターフェイス定義
- パスとリソースのマッピング機能
- HTTPメッセージを抽象化して扱う機能
を提供しています。
さらに、REST APIを持つアプリケーションを作成する際によく使われると思われる、
以下のようなリソースクラスの実装をあらかじめ用意しています。
- RDBMSにJSON形式のデータを保存する実装(の抽象クラス)
- ファイルをURL上に紐づけて保存していくファイルシステムのような実装
1. 準備
それでは実際に動かしてみましょう。
1.1 事前に用意するもの
サンプルを動作させるために必要なものは以下の通りです。
- JDK 1.7.0以降
- Eclipse 3.7以降
- Gitプラグイン(Eclipseで動作すればOKです)
- Tomcat 7.0以降
- 何らかのRESTクライアント
- 例)Firefoxのアドイン「RESTClient」 https://addons.mozilla.org/ja/firefox/addon/restclient/
- 例)Chromeの各種アドイン https://chrome.google.com/webstore/search/rest%20client
なお、このチュートリアルでは、EclipseやRESTクライアントの細かい操作方法の記載は省略します。
1.2 サンプルプロジェクトのダウンロード
hifive Resource Framework本体は、次のところから入手可能です。
* https://github.com/hifive/hifive-sync-server
本体のソースコードはEclipseのプロジェクトの形で提供されています。
動作させるためには他にも設定が必要です。
(ソースツリーの中に設定ファイル例も含まれています)
動作させるためのサンプルプロジェクトを以下の場所に用意しています。
* https://github.com/hifive/hifive-sync-server/tree/master/resourceFrameworkSample
サンプルは、EclipseのTomcatプラグインを用いたプロジェクトの形式になっています。
1.3 ivyによる依存ファイルのダウンロード
まずは、ダウンロードしてきたプロジェクト直下にある、ivy_build.xmlをantで実行します。
これにより、必要なライブラリが自動でダウンロードされていきます。
なお、hifive Resource Frameworkは主に以下のようなフレームワークを
利用しています。
- Spring MVC
- Spring Data JPA
- Spring Security
2. サンプルによる動きの確認
この項では、サンプルプロジェクトにあらかじめ組み込まれている
Personリソースを用いて、基本的なCRUD操作を習得します。
サンプルプロジェクトでPersonリソースを動作させる際に、
変更が必要な設定ファイルは特にありません。
ivyの実行が終わってることを確認したら、早速Tomcatを起動してみましょう。
2.1 データの取得
起動したら、まずは、
http://[あなたのPCのホスト名またはIPアドレス]:8080/hifive-resource-sample/resources/person/
にアクセスしてみましょう。
ブラウザの認証ダイアログが表示されますので、
ユーザ名「hifive」、パスワード「hifive」でログインします。
ログイン後、「[]」だけが表示されれば成功です。
(念のためコンソールに例外が出ていないことを確認してください。)
こんどは、RESTClientから動かしてみます。
まずはデータの一覧を取得してみましょう。
URL:http://localhost:8080/hifive-resource-sample/resources/person/
ヘッダ:
ボディ:
(空)
↓
レスポンス:
Status Code: 200 OK
Content-Type: application/json;charset=UTF-8
(中略)
[]
このAPIは、personというリソースに投入されているデータの一覧を取得するAPIです。
本来は、入っているデータのIDの一覧がJSON配列で返ってくるのですが、
今はデータが何も入っていないので、JSONの空配列を表す、「 []」 が返ってきています。
2.2 データの登録、更新、削除
続いて、データを新しく登録してみましょう。
Person型には
- personId : IDに使う文字列
- name: 実名
- age: 年齢
- organization: 所属
の各フィールドがあります。
この各フィールドを持ったJSONをURLに対してPUTすることで、
データを登録することができます。
次のようなリクエストを送ります。
URL:http://localhost:8080/hifive-resource-sample/resources/person/1
ヘッダ:
Content-Type: application/json
Body:
{
"personId":"1",
"name":"hi five",
"age":"20",
"organization":"hifive developer team"
}
レスポンス:
Status Code: 200 OK
Content-Length: 1
Content-Type: text/plain;charset=ISO-8859-1
Date: (略
Server: (略
Set-Cookie: (略
X-resourceFw-storageid: (略
X-resourceFw-synctime: (略
1
問題がなければ、レスポンスコードは「200 OK」、レスポンスのボディ部には、
「書き換えたリソースアイテムのID」が返ってきます。
それでは、先ほど登録したデータを取得してみましょう。
URL:http://localhost:8080/hifive-resource-sample/resources/person/1
ヘッダ:
ボディ:
(空)//
レスポンス:
Status Code: 200 OK
Content-Type: application/json;charset=UTF-8
(中略)
{"personId":"1","name":"hi five","age":20,"organization":"hifive developer team"}
さらに、先ほどの一覧表示を見てみましょう。
URL:http://localhost:8080/hifive-resource-sample/resources/person/
ヘッダ:
ボディ:
(空)
↓
レスポンス:
Status Code: 200 OK
Content-Type: application/json;charset=UTF-8
(中略)
["1"]
一覧に1個データが増えています。これで登録することができました。
登録したデータを更新してみましょう。
同じURLに値を変えたリクエストを送ることで、値を書き換えることができます。
URL:http://localhost:8080/hifive-resource-sample/resources/person/1
ヘッダ:
Content-Type: application/json
Body://
{
"personId":"1",
"name":"hi five",
"age":"30",
"organization":"hifive foundation"
}
レスポンス:
Status Code: 200 OK
Content-Length: 1
Content-Type: text/plain;charset=ISO-8859-1
Date: (略
Server: (略
Set-Cookie: (略
X-resourceFw-storageid: (略
X-resourceFw-synctime: (略
1
URL:http://localhost:8080/hifive-resource-sample/resources/person/1
ヘッダ:
ボディ:
(空)//
レスポンス:
Status Code: 200 OK
Content-Type: application/json;charset=UTF-8
(中略)
{"personId":"1","name":"hi five","age":30,"organization":"hifive foundation"}
続いて削除してみましょう。DELETEメソッドを使います。
URL:http://localhost:8080/hifive-resource-sample/resources/person/1
ヘッダ:
ボディ:
(空)
レスポンス:
Status Code: 200 OK
Content-Length: 1
Content-Type: text/plain;charset=ISO-8859-1
(中略)
1
成功すれば、レスポンスコード200とともに、消去したデータのIDが返ります。
以上、一通りリソースに対しての読み書きの操作を行ってみましたが、
リソースにはこの他にも、以下のような操作が定義されています。
- アイテムの存在確認 (exists)
- リソース内のアイテムの個数確認(count) など
詳しくは、リファレンスを参照してください。
3. RDBMS接続用のリソースの作成
では、このサンプルと同じような動作をするリソースクラスを実装してみましょう。
ここでは、イベント(コンサートやスポーツの試合など)を模したデータ「Event」を登録するサンプルを作ってみます。
このフレームワークは、URL/メソッドと実際に動作するクラス/メソッドの対応づけを
自動で行いますので、ユーザが実装するのはリソースの動作を記載した、
「リソースクラス」だけです。
リソースは、データを永続化する手法を自由に実装することができますが、
ここでは、このフレームワークが用意している抽象クラスを使って、
Spring Data JPAを使ったデータベースへのデータ格納のサンプルを作ります。
3.1 実装方法の概要
このフレームワークにおいては、リソースを実装する方法は
大きく次の3つがあります。
- BasicResourceインターフェイスを実装する方法
BasicResourceインターフェイスを実装したクラスを作成し、各メソッドに
それぞれのアクションに対応する実装を記述します。
実装を1から記載する必要がありますが、送るデータ、受けるデータ形式を
自由に決めることができ、データベース以外を保存先にすることもできます。
また、あらかじめ定義されているもの以外のアクションを定義することができます。 - AbstractCrudResourceクラスを拡張する方法
AbstractCrudResourceクラスは、Spring Data JPAを利用して、
データベース上の行をほぼそのままリソースアイテムとする抽象クラスです。
このクラスを継承することで、データベースにデータを保存するリソースを、
わずかな記述で実装することができます。 - GenericUrlTreeFileResourceを拡張またはそのまま利用する方法
URLのパス部分をファイルシステムに見立てて、ディレクトリ/ファイルを
サーバ上に保存するためのリソース実装です。
この実装を使うことで、Webフォルダのような、サーバ上にファイルを保存する
仕組みを実現できます。
ここでは、DBに接続するための実装である、AbstractCrudResourceクラスを
使って、データをデータベースに保存する実装を作成します。
AbstractCrudResourceを利用する場合に、
最小限、実装が必要なものは次の通りです。
- リソースクラス
- 操作対象の処理を記載します。
AbstructCrudResourceを継承します。
サンプルの場合は、PersonResourceクラスがこれに当たります。
- DTOクラス
- 操作対象のリソースアイテムについて、データ構造を定義します。
サンプルの場合は、Personクラスがこれに当たります。
- JPAリポジトリとエンティティクラス
- Spring Data JPAで利用するものです。
サンプルの場合は、それぞれPersonRepository、Personがこれに当たります。
サンプルのPersonにはこの他にも、AbstractCrudResourceで使える
いくつかのクラスが含まれています。
3.2 データモデルの設計
イベントに含まれるデータを次のように定義します。
- イベント名
- 日時
- 場所
- 出演者
これらに対して、
- 1件のデータ読み取り
- データのリスト取得
- 書き込み(PUT)
- 削除
ができることを要件と仮定しましょう。
3.3 ソースコード
それでは早速実装してみましょう。
リソース定義クラス(Event.java)
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="EVENT")
public class Event implements Serializable {
/** SerialVersionUID */
private static final long serialVersionUID = -369760912817634025L;
/** イベントID */
@Id
private String eventId;
/** 開催日 */
private Date date;
/** イベント名 */
private String name;
/** 場所 */
private String place;
/** 出演者 */
private String actor;
/** お値段 */
private int price;
/*
* 以下省略。 serialVersionUID以外すべてのフィールドに
* setterとgetterを作成します
*/
実装のポイント
Spring Data JPAのエンティティクラスとして実装します。
- @Entityアノテーションを付与します。これは、Spring Data JPAのリポジトリに、
このクラスが保管対象であることを示すものです - IDとなるフィールドに@IDアノテーションを付与します。これはSpring Data JPAの
機能ですが、AbstractCrudResourceもこのアノテーションを参照してIdを探します。 - Serializableを実装します。
JPAリポジトリ
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
* Eventクラスに対するJPAリポジトリ。
*/
public interface EventRepository extends JpaRepository<Event, String>, JpaSpecificationExecutor<Event> {
}
実装のポイント
- Spring Data JPAのリポジトリを拡張したインターフェイスとして定義します。
- JpaRepositoryインターフェイスに加えて、JpaSpecificationExecutorを拡張してください。
これはAbstructCrudResourceのfindByQueryを使うときには必須です。
Resourceクラス
@ResourceClass(name = "event")
public class EventResource extends AbstractCrudResource<Event> {
@Autowired
private EventRepository eventRepository;
@Override
protected JpaRepository<Event, String> getRepository() {
return eventRepository;
}
}
実装のポイント
このフレームワークの規約に則って実装します。
- ResourceClassアノテーションを付加します。これにより、フレームワークは
このクラスがリソースクラスであることを認識します。 - AbstructCrudResourceを継承します。
- 型変数(クラス定義の<>の中)にはリソースアイテムのクラス名を記述します。
今回はリソースアイテムを表すのはEvent型ですので、Eventと記述します。 - リソースアイテム型を扱うJPAリポジトリを用意し、getRepository()がそれを返すようにします。
今回はリポジトリとしてEventRepositoryクラスを使いますので、EventRepositoryクラスの
インスタンスを返すように記述します。
3.4 動作設定
spring/resourceConfの下に event.xmlを置きます。
(名前は*.xmlであれば何でもOKです。person.xmlをコピーすると手早いでしょう。)
次のように設定します。
<context:component-scan
base-package="[Resourceクラスを置いたパッケージ名]" />
<!-- JPAリポジトリのパッケージ設定 -->
<jpa:repositories base-package="[JPAリポジトリを置いたパッケージ名]"
transaction-manager-ref="transactionManager" />
さらに、root-context.xmlを書き換えます。
<!-- 以下のListに作成したJPAリポジトリが入っているパッケージを追加 -->
<property name="packagesToScan">
<list merge="true">
<value>com.htmlhifive.resourcefw.sample.resource.person</value>
<value>com.htmlhifive.resourcefw.file.metadata</value>
<value>[JPAリポジトリを置いたパッケージ名]</value> ← この行を追加
</list>
</property>
</bean>
3.5 実行
最後に、ビルド、Tomcat再起動を行います。
各Resourceクラスは、デフォルトで「XXXXResource」の
「XXXX」の部分の名前で、URLの/resources以下に自動的にマッピングされます。
このアプリケーションにおけるURLの配置は以下のようになります。
/resoures リソースがマップされるパス
/person →サンプルのpersonResourceにマップ
/event →先ほど作ったEventResourceにマップ
/urltree →ファイルを扱うリソース(デフォルトで無効。後の章で説明します。)
: それ以外にも作成すればここ以下にマッピングされていきます。
:
/static HTML、JSファイルなどの静的なコンテンツの置き場所
/images/
したがって、今回作ったリソースは
http://localhost:8080/hifive-resource-sample/resources/event/[リソースアイテムID]
にマップされます。
それでは早速実行してみましょう。
URL:http://localhost:8080/hifive-resource-sample/resources/event/
ヘッダ:
ボディ:
(空)
↓
レスポンス:
Status Code: 200 OK
Content-Type: application/json;charset=UTF-8
(中略)
[]
URL:http://localhost:8080/hifive-resource-sample/resources/event/1
ヘッダ:
Content-Type: application/json
Body:
{
"eventId":"1",
"date":"2013/07/21 17:00",
"name":"nanika no concert",
"place":"dokoka",
"actor":"dareka",
"price":50000
}
レスポンス:
Status Code: 200 OK
Content-Length: 1
Content-Type: text/plain;charset=ISO-8859-1
Date: (略
Server: (略
Set-Cookie: (略
X-resourceFw-storageid: (略
X-resourceFw-synctime: (略
1
URL:http://localhost:8080/hifive-resource-sample/resources/event/1
ヘッダ:
ボディ:
(空)//
レスポンス:
Status Code: 200 OK
Content-Type: application/json;charset=UTF-8
(中略)
{"eventId":"1","date":1374393600000,"name":"nanika no concert","place":"dokoka","actor":"dareka","price":50000}
3.6 さらなる拡張
AbstractCrudResourceでは、これ以外の拡張ポイントとして
以下のようなものを用意しています。
- findByQueryのクエリ
- デフォルトではfindByQueryは常に全件を返しますが、
ResourceQuerySpecificationクラスを実装することで、
クエリを解釈できるようになります.
- IDの採番ポリシー変更
- AbstructCrudResourceは、create時(URLを指定しないでリソースアイテムを作成する時)に
IDが自動採番されます。デフォルトではランダムUUIDとなっていますが、
createNewId()をオーバーライドすることでIDの採番ポリシーを変更できます。
4. ファイルを保存するリソースの利用
4.1 概要
GenericUrlTreeResourceは、URL上にファイルシステムのようなものを公開し、
それらに対してREST操作ができるように実装されたリソースです。
デフォルトでは、ファイルはサーバ上の特定のディレクトリにに保存されますが、
実装を切り替えることで、それ以外(他のサーバや各種ストレージなど)に保存することもできます。
4.2 設定
サンプルに次の設定を行うことで利用することができます。
- src/java/resources/spring/resourceConf/genericurltree.xml.undef のファイル名を「genericurltree.xml」に変更
- src/java/resources/spring/appConf/fileresource.propertiesの「base.dir」に、アップロードされたファイルの置き場所を指定
※設定を行ったら再起動してください。
主なアクションとその動作は以下の通りです。
- 対象がファイルの場合
操作 | メソッド | URL指定(例) | ボディ | 正常時のレスポンスコード | レスポンスの内容 |
---|---|---|---|---|---|
ファイルの取得 | GET | /resources/urltree/hoge.txt | なし | 200 | ファイルの内容 |
メタデータ取得 | GET | /resources/urltree/hoge.txt?metadata | なし | 200 | ファイルのメタデータ(application/json) |
メタデータ操作&br;(所有者グループ変更、権限変更など) | PUT | /resources/urltree/hoge.txt?metadata | メタデータ(※2) | 200 | なし |
作成/更新 | PUT | /resources/urltree/hoge.txt | ファイルの中身 | 201(作った場合)&br;または200(更新した場合) | なし |
削除 | DELETE | /resources/urltree/hoge.txt | なし | 200 | なし |
- 対象がディレクトリの場合
操作 | メソッド | URL指定例 | ボディ | 正常時のレスポンスコード | レスポンスの内容 |
---|---|---|---|---|---|
ディレクトリ内の一覧取得 | GET | /resources/urltree/hoge/ | なし | 200 | ファイルのリスト |
メタデータ取得 | GET | /resources/urltree/hoge/?metadata | なし | 200 | ディレクトリのメタデータ(application/json) |
メタデータ操作(所有者グループ変更、権限変更など) | PUT | /resources/urltree/hoge/?metadata | メタデータ(※2) | 200 | なし |
作成 | PUT | /resources/urltree/hoge/?type=dir (※1) | なし | 201 | なし |
削除※3 | DELETE | /resources/urltree/hoge/ | なし | 200 | なし |
- ※1 type=dirの指定を忘れると、0バイトのファイルが作成されてしまいます。
- ※2 メタデータ取得で送ってきたJSONと同じ形式を送り返します。ただし、現在の実装では所有者/グループ、権限以外の変更は無視されます。
- ※3 中にファイルやディレクトリがあると削除できません。
では早速実行してみましょう。
URL:http://localhost:8080/hifive-resource-sample/resources/urltree/
ヘッダ:
ボディ:
(空)//
レスポンス:
Status Code: 200 OK
Content-Type: application/json;charset=UTF-8
(中略)
[]
もちろん最初は何もありません。テキストファイルを1個アップロードします。
URL:http://localhost:8080/hifive-resource-sample/resources/urltree/hoge.txt
ヘッダ:
Content-Type: text/plain
ボディ:
hoge
hoge
hoge
レスポンス:
Status Code: 201 Created
Content-Type: application/json;charset=UTF-8
(中略)
ボディ:(空)
URL:http://localhost:8080/hifive-resource-sample/resources/urltree/hoge.txt
ヘッダ:
ボディ:
(空)//
レスポンス:
Status Code: 200 OK
Content-Type: application/json;charset=UTF-8
(中略)
hoge
hoge
hoge
base.dirで指定したディレクトリ以下にファイルが保存されていれば成功です。
4.3 拡張する場合
GenericUrlTreeFileResourceは次のような拡張が可能です。
- 認可モデルの拡張 → AuthorizationManager
- ファイルの保存先の変更 → ContentsPersisterを実装
それぞれ、genericurltree.xmlで利用するクラスを切り替えることが
できます。リファレンスを参照してください。
5. その他
5.1 DBの接続先変更
サンプルプロジェクトの設定では、組み込みデータベースのH2を使用しており、
Tomcatを再起動するとデータは消えてしまいます。
DBの接続先設定を変更するためには以下の部分を変更します。
src/main/webapp/WEB-INF/web.xml:
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>embedded → rdbmsに変更</param-value>
</context-param>
src/main/resources/spring/jdbc.properties:
jdbc.url=[JDBCのURL] 例) jdbc:oracle:thin:@192.168.11.11:1521:xe
jdbc.username=[JDBCユーザ名]
jdbc.password=[JDBCパスワード]
jdbc.dialect=[HibernateのDialectクラス]※ 例)org.hibernate.dialect.Oracle10gDialect
※Hibernateにおいて、RDBMSによるSQLの違いを吸収するためのクラス。
以下のページを参考に、RDBMSの製品によって指定してください。
http://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/dialect/package-summary.html
その他、必要であれば、src/main/resources/spring/database.xml に
DB関連の設定が記載してありますので、変更してください。
5.2 ユーザを増やす
src/main/resources/spring/users.propertiesにユーザを追加してください。
順に「ユーザ名=パスワード(平文),ロール名」です。
tada=tada,ROLE_ALL
なお、ユーザ管理にはSpring Securityを使用していますので、
Spring Securityが提供する認証方法を利用することができます。
詳しい設定は、Spring Securityのマニュアルを参照してください。
5.3 他のコントローラを使う
このフレームワークを利用したアプリケーション内で、SpringMVCの他のControllerを
動作させることができます。
実装したコントローラを置いたパッケージ名を、
src/main/resources/spring/appServlet/controllers.xmlに以下のように指定してください。
<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-3.1.xsd">
<context:component-scan base-package="com.htmlhifive.resourcefw.sample.ctrl" />
<context:component-scan base-package="[使いたいコントローラのパッケージ名]" /> <!-- ←この行を追加 -->
</beans>
参照