Regional SCRUM GATHERING Tokyo 2016へ参戦

2日間で、アジャイルスクラム関連のセッションが開催されるイベントの
Regional SCRUM GATHERING Tokyo 2016へ参戦。
Regional SCRUM GATHERINGは、スクラムの初心者からエキスパート、ユーザー企業から開発企業など
立場の異なる様々な人々が集まる学びの場です。
スクラムマスターやプロダクトオーナーやトレーナーなど多くの人が集まってます。
今回は、CSMを取得して初めて参加しました。
2016.scrumgatheringtokyo.org
初日なんですが、まさかの大雪で、朝起きたら大変なことにwww
とりあえず駅へ...入れないし、電車が壊滅状態でたどり着けず断念www
スクラムのスケールの話とか聞きたかったのに...。
f:id:chronos_snow:20160119231200j:plain
2日目からの参加になってしまったwww
2日目のkeynoteは、Scrum AllianceのCEOやエッセンシャルスクラムの著者の方の話でした。
エッセンシャルスクラムの著者の方の講演は、
コンポーネントチームやフューチャーチームの話は聞けて良かったと思います。
f:id:chronos_snow:20160119230738j:plain
今回、聞いたセッションは、下の内容です。

Microsoftが実践したScrum導入7年間の旅、そしてDevOpsへの進化
  • Microsoftがかなりの時間を掛けてウォーターフォールからアジャイルへ移行。
  • DevOpsは、開発以外にも運用(オペレーション)も含んでいる。
  • DevOpsで、コードのデプロイが30倍スピードアップwww。
  • DevOpsで、パフォーマンスの低い環境に比べてリードタイムが200分の1に短縮www。

docs.com

Agile人材の評価とキャリアプラン

www.slideshare.net

あなたのチームの「いい人」は機能していますか?
  • 障害や運用に追われて、本タスク出来てない人とかいる。
  • まわりにくらべて自分の成果は小さい。
  • 技術力が向上していない。
  • プロダクトにコミットできない。

www.slideshare.net

あじゃいる時代のソフトウェア品質保証 〜DevSQAの提案〜
  • 山中教授似すぎwww

www.slideshare.net

Customer Expectations Management of Scrum スクラムにおける事前期待のマネジメント
  • プロダクトオーナーよりの内容。
  • サービスの品質。
  • サービスサイエンス
  • 期待のマネージメント。

www.slideshare.net

認定スクラムマスター研修を受けたのでその整理

認定スクラムマスター研修とは?

認定スクラムマスター研修(CSM)は、認定スクラムトレーナー(CST)によって開催される Scrum Alliance 認定研修です。
本研修を通じて認定スクラムトレーナー(CST)に認定スクラムマスター(CSM)の能力を認められたら、Scrumm Alliance に登録されます。
Scrumm Alliance に登録されるとオンライン試験の受講資格が与えられ、このオンライン試験に合格すると認定スクラムマスター(CSM)と認定されます。
認定スクラムマスター (CSM) は、スクラムマスターとして必要なスクラムの基礎を理解していることを示します。

研修内容

受講するにあたっての自分の中の疑問をこの研修で解決できたらいいと思ったこと。

  • 全員がなっとくして、共有の理解を得るには?
  • 設計は、いつやるのか?
  • ゴールを意識するには?
  • 生産性とは?

アジャイルとは?

アジャイルソフトウェア開発宣言
f:id:chronos_snow:20151223021230j:plain:w300
アジャイル宣言の背後にある原則
f:id:chronos_snow:20151223021221j:plain:w300

アジャイルとは、価値があるものを生み出す為に、より良い開発方法を見つけ出そうとしている時のことをさす。
それ以外の時は、それはアジャイルではない。
スクラムだろうがXPだろうがWFだろうが、 より良い開発方法を見つけ出そうとしている時はアジャイルであり、それ以外はアジャイルではない。

スクラムは何を解決してくれるフレームワークなのか?

  • 開発効率向上
  • 状況の変化に強い
  • 成長できる

そういったフレームワークではありません。
そういった効果があったとしてもそれは、そのチームが努力した結果でしかありません。
スクラムは下記のことしかできません。

スクラムが有効なのは?

スクラムは、下記のような単純なものや既に実現方法が明確に決まっているものには向きません。

  • 実現内容が単純な場合
  • 実現内容が変化しない場合
  • 実現方法が明確に決まっていて、失敗する可能性が小さい場合

f:id:chronos_snow:20151223021630p:plain:w300

そのプロジェクトが単純なほどウォーターフォールなどが向いています。
また、スクラムチームの生存期間が3ヶ月以下の場合はスクラムで進めても効果が薄い(統計データー的にスクラムチームが学習したものが活用されるのが3ヶ月経過してから)。
上記以外ならスクラムはほぼ有効に活用できる。

Working Agreement

チームルールを全員で議論して、その決めたルールを明文化します。
このルールは、全員が納得した内容でなければなりません。
スクラムマスターは、この時、議論がカオス化しないようにきをつける必要があり、
議論内容の質と時間のバランス(指標的なもので判断)をとりながら全員が納得した内容になるようにしなければなりません。
Working Agreementを作成する時の注意事項としては、下記のようなものがあります。

  • アクションに繋がらないスローガンのようなものはダメ。(例:相手を尊重する、等)
  • ルールは具体的に、判断できるようなものを目指す。計測できることが望ましい。
  • ルールを決めるためのルール、ルールを変更するためのルールなども決めておく。

スクラムのルールとしては、まだ入っていませんが、現在、ルールとして入れるか検討中だそうです。

スクラムのルール

f:id:chronos_snow:20150509230758p:plain:w300

3本柱

透明性
現在の状況が全員に対して可視化されていること。
また、チームルールや開発手法なども全員が納得して共通理解を持っていること。

  • 進捗状況を全員が共有している(完了/未完了?完了の場合は作成物は?)。
  • プロセスの用語を全員が理解している(誰に聞かれても明確に説明できる状態)。
  • 開発手法に関して全員が納得して共通理解を持っている(誰に聞かれても明確に説明できる状態)。
  • チームルールに関して全員が納得して共通理解を持っている(誰に聞かれても明確に説明できる状態)。
  • 作業内容に関しての完了の定義を全員が共有している(誰に聞かれても明確に説明できる状態)。

下のような曖昧な状態は、認められない。

  • もうすぐできます。
  • 90%ぐらいです。
  • こんな感じで開発するんですよね。

検証
成果物やプロセスを検証することで、変化を検知します。
また、 新しい開発手法や改善などの取り組みに関しても検証します。

  • 成果物は、想定通りなのか?
  • 取り組んだ改善方法は、想定通りの効果が出たか?
  • 新しい開発手法は、想定通りの効果が出たか?

適応
プロセスの不備が許容値を超え、成果となるプロダクトを受け入れられないと判断された場合、
プロセスやその構成要素(仕様や開発手法及び改善への取り組みなど)を調整する必要がある。
調整はできるだけ早く行い、これ 以上の逸脱を防がなければいけない。

  • 実実を元になんらかの調整や改善を行います。
  • 誰にでも明確に理解できるもの(数値化されているなど)で、全員が納得し共通理解を持っているものを適応させて行きます。
  • 根拠が曖昧なものを元に適応することはありません。

役割(ロール)

プロダクトオーナー

  • チームのROIを最大化する。
  • 誰にでも費用対効果を説明できる。
  • 手法やコストなどを総合的に判断できる。
  • チームのコストを最小化(下げる)する。
  • チームが誤った方向に行かないようにする。
  • 多くの情報を収集できる。
  • 内外の状況を察知できる。
  • 他のチームや組織などと交渉できる。

スクラムマスター

  • スクラムが適切に活用される状態を作る。
  • スクラムが適さない場合は別の方法を提案できる。
  • チームの生産性を3ヶ月で46%以上向上させる。
  • 自律的なチームを2ヶ月で作る。
  • 自律的なチームを維持(守る)する。
  • ベロシティを最大の部分で安定させる。
  • 曖昧なことは明確化(数値化)する。

チーム

  • 約束されたことを必ず実現する。
  • チームの生産性を高める努力をする。
  • 個人の能力を高める努力をする。
  • 出来ないことは出来ないと主張する。
  • ベロシティを最大の部分で安定させる。

アーティファクト(成果物)

プロダクトバックログ
プロダクトに必要なフィーチャ・機能・要求・要望・修正がすべて並べられた一覧です。
プロダクトオーナーは、プロダクトバックログの内容及び、並び順に責任を持っています。
プロダクトバックログの見積もりは、チームが行います。
プロダクトバックログのアイテムはユーザーストーリー形式で書く必要はありませんが、ユーザーストーリー形式で書かれるケースが多いです 。

  • ユーザーストーリー(としてが欲しい。なぜならだから)
  • 1つのアイテムにアクセプタンスクライテリア(受け入れ基準)を1つ以上定義する。
  • 1つのアイテムにやらないことも必要なら定義する。

f:id:chronos_snow:20151223022415p:plain:w300

WHO

  • ユーザーロールを意識しますが、必ずそれにしなければならないわけではありません。
  • 外部システムやAPI又は、コンポーネントでも問題はありません。

WHAT

  • どういった機能を明確に定義します。
  • 曖昧な表現や言葉はNGです。
  • 誰でも理解できるものでなければなりません。

WHY

  • 価値を明確に定義します。
  • 曖昧な表現や言葉はNGです。
  • 誰でも理解できるものでなければなりません。

ユーザーストーリーを適切なサイズ(機能の最小の単位)にまとめるのは難しく、慣れが必要です。
ユーザーストーリがINVESTの条件を満たしているならそれは良いユーザーストーリです。
INVESTとはユーザーストーリーの条件を満たす略語です。
Independent(依存がない又は少ない)   アイテムがそれぞれ独立していること。

  • 依存関係(前後も含む)は排除。
  • 依存度が高いと見積が難しい。

Negotiable(交渉可能)

Valuable(価値がある)

  • ステークホルダーにとって価値がある。
  • ビジネス(会社)にとって価値がある。
  • チームにとって価値がある。

Estimable(見積可能)

  • 見積可能。
  • 見積できるぐらいの粒度。

Small(最小単位)

  • 適切な大きさ。
  • 十分に分割されている。

Testable(テスト可能)

  • テスト可能。
  • 受入テストを定義可能

ストーリーは、INVESTの条件を満たして、幾つかのストーリーを組み合わせると1つの機能になるようにする。
f:id:chronos_snow:20151223022803p:plain:w300

スプリントバックログ
スプリントで達成するプロダクトバックログのアイテムを決定後、チームは、それをインクリメントに実現する為の方法を決めます。
決めた方法を実際の作業(タスク)に細分化します。
チームはどのようにスプリントゴールを達成し、どのように期待されるインクリメントを作成するかをプロダクトオー ナーとスクラムマスターに説明できなければなりません。
スプリントが停滞するようなもは、スプリントバックログには入れません。
そのスプリントで達成しなければならないプロダクトバックログのアイテムの設計や検討などは、
スプリント開始前に明確なっている必要があります。

インペディメントリスト
プロジェクトでのポジティブ、ネガティブなことを記載するリスト。
但し、個人攻撃をするようなデリケートな問題は慎重に扱う必要があります。
スクラムマスターは、インペディメントリストの内容の内容及び、並び順に責任を持っています。

セレモニー

スプリントプランニング

  • スプリントでなにを実現するかを明らかにする(チームとPOがそれをコミットしている状態)。
  • 実現方法が明確になっている(設計が完了している)。

デイリースクラム

  • チームが学習したこと共有する。
  • 前回から今回までの間の内容を共有する。
  • 今回から次回までに何を行うのかを共有する。
  • スプリント内外の気になることを共有する。

スプリントレビュー

  • スプリントで約束されたことが実現出来たか?
  • プロダクトを実際操作しさらに良くできないかを議論する。

スプリントレトロスペクティブ

  • 今回のスプリントはどうだったか?
  • 次回のスプリントでチームが生産性を向上させる為の改善を1つ以上決める。

プロダクトリファイメント

  • 追加や変更されたもの内容確認や見積もりなど。
  • スプリントの中長期的な話。
  • スプリントの5%~10%ぐらいのコスト。

その他

スプリント
スクラムの中心はスプリントである。
スプリントは、利用可能なリリース判断可能なプロ ダクトインクリメントを作るための、1 か月以下のタイムボックスがある。
スプリントは、開発作業を 行う連続した期間である。
スプリントが終了すると、新しいスプリントが開始される。

  • スプリントゴールに悪影響を及ぼすような変更を加えない。
  • 品質目標を下げない。
  • 学習が進むにつれてスコープが明確化され、プロダクトオーナーと開発チームの交渉が必 要になる可能性がある。

スプリントストップ
スプリントが停止すること。
競合他社が同様のプロダクトを先にリリースして、優位性が確保できない場合や資金が尽きてしまった場合などがある。
プロダクト
スプリント終了後には、出荷可能な製品を必ず作ること。
但し、このルールを完璧に守れているチームは世界に存在しないらしい。
アクセプタンスクライテリア
プロダクトバックログに記載されているアイテムの1つに対して必ず1つ以上定義する。
チームに開発したプロダクトがユーザーやクライアントに受け入れてもらえるかを考える機会を与える為に存在する。
また、やってはいけないことを必要なら記載することもできる。
Done
プロダクトバックログアイテムやインクリメントの「完成」を決めるときには、全員がその「完成」の 意味を理解しておかなければいけない。
スクラムチームによってその意味は大きく異なるが、 作業の完了についてメンバーが共通の理解を持ち、透明性を確保しなければいけない。
これは、スクラムチームの『「完成」の定義』と呼ばれ、プロダクトインクリメントの作業が完了したかどうかの評価に使われる。
スクラムチームのDoneの定義の例としては、下記の様なものがあると思います。

Doneの逆のUndoneというものがあります。
Doneは、毎スプリント完了しているものなので、例えば、初期の頃のスプリントでは、単体テスト結合テストしか完了できたいとしたら、
それ以外は、すべてUndoneとなります。
Undoneは、未完了の作業のことを指します。
Undoneが多いとスケジュールが遅延したり、想定しないリスクが増大します。
Undoneを通常のスプリントで解消できなくなる状況になってしまったら、
Undone作業のみを実施するためだけのリリーススプリントという特殊なスプリントを実施しなければならくなります。
このUndoneは、技術的負債と言われます。
Undoneを解消するとその項目は、Doneに入りますが、毎スプリントごとに最小のコストで解消できてないとDoneには入れられません。
このUndoneを戦略的にどうするかの責任は、プロダクトオーナーになります。
スクラムマスターは、プロダクトオーナーをサポートしながら、Undoneを減らせるようにチームに働きかける必要があります。

見積に関して

人間の特性

  • 未来を予測する能力は低い
  • 絶対値での見積をする能力は低い

未来を予測する能力は低い

  • 未来になればなるほど予測する精度が落ち当たらない。

絶対値での見積をする能力は低い

  • 床から天井まで何センチメートールですか?
  • この紙は何平方メートルですか?
  • 床から天井まで、このダウンボール箱何個分ですか?
  • この紙は、あの紙の何倍ですか?

絶対値での見積より相対値での見積の方が誤差が少ない。
スクラムでの見積方法
見積は、未来になればなるほど見積精度が落ちます。
また、時間をかなり掛けても見積精度が100%になるようなことはありません。
逆に見積精度が落ちる場合もあります。
f:id:chronos_snow:20151223023600p:plain:w300
スクラムでの見積もり方法は2つあります。

  • 相対見積
  • 絶対見積

プロダクトバックログの見積方法
プロダクトバックログには、見積方法は、相対見積(ポイント)になります。
プロダクトバックログの1つ1つのアイテムにポイントを付けていきます。
ポイントは、そのアイテムの難易度などではなく、作業量によって決定されます。
プロダクトバックログは、3つのエリアを定義します。

  • 詳細なエリア
  • 荒いエリア
  • その他のエリア

プロダクトバックログに設定した詳細なエリアと荒いエリアの2つエリアは、2:8法則で最大ポイントを設定します。
f:id:chronos_snow:20151223023711p:plain:w300

2:8の法則
ほとんどの現象は、2:8ぐらのばらつきがあります。
全体の2割が優れたものなら、残りの8割の状況で優れた効果が出るといったものです。 

詳細なエリアは、スプリントで達成していくアイテムが入るエリアです。
荒いエリアは、詳細レベルまで落とし込めていないアイテムが入るエリアです。
その他のエリアは、見積できるレベルになっていないか?気にしないものが入るエリアです。
詳細なエリアの最大ポイントの設定と基準となるアイテムを決めます。
見積は、基準となるアイテムと比べて他のアイテムはどれぐらいか?で相対的に見積をします。
また、詳細なエリアが最大2ポイントの場合、荒いエリアは、その4倍の8ポイントが最大になります。
プロダクトバックログのアイテムの中で、その粒度を超えているものは、この時点で分割してどちらかのエリアに入れるか、
分割できない場合は、その他のエリアに入れるか判断します。
プロダクトバックログのアイテムの見積(ポイントを設定)の方法は、いろいろありますが、プランニングポーカーでやることが多いです。
プランニングポーカーは、あるアイテムのポイントを全員がカードを出し合いポイントを決定するやり方で、
出したポイントに差異があった場合、最大の人が理由を発表後、最小の人が差分を発表して、認識の違いを確認します。
最大と最小の人以外は、発言することはできません。
認識の違いを確認したら再度、全員がカードを出し合い、全員が一致するまで行います。
スプリントバックログの見積方法
直近のスプリントで達成しなければならないプロダクトバックログのアイテムに関して、
それを達成する為の作業内容を洗い出し、絶対見積で見積を行います。
直近の作業内容が詳細になればなるほど見積精度が良くなると言われているので、
スクラムマスターは、1つのタスクが0.5時間から1時間以内に収めることを目指します。

自律的なチームとは?

自律的なチームとは、下記の特性を持っています。

  • 明確なゴールを持ち、ブレない方針を持っている。
  • チームの方針について議論できる。
  • チームに個人として結果をフィードバックできる。
  • どの領域で自分が役立つか考え(バウンダリー)行動する。
  • 一瞬(0.1秒)で現状を把握して、判断して行動する。
  • チームがチームを管理する(スクラムマスターは管理者ではない)。
  • 個人は、生産性を向上する為、自ら考え努力する。

チームがチームを管理しながら改善を行いチームとして成長して行きます。
また、個人としても成長します。
スクラムマスターは、そのような自律的なチーム2ヶ月で作り上げなければならない。
スクラムマスターは、自律的なチームを維持(守る)する必要があります。
自律的なチームを維持(守る)するということは?

  • スクラムマスターは、なんらかの理由で、メンバーがいなくなったりしても安易にチームに人を追加しません。
  • スクラムマスターは、誰かが新たしく入って来たとしてもその人を安易にチームに追加したりしません(プロジェクトは一緒でも自律的なチームとその人と分けて考える)。

スクラムチームに人を追加するということは、一度、スクラムチームが解散したことを意味します。
その場合、新しいスクラムチームになるので、自律的なチームが持つ特性を最初から構成しなおします(2ヶ月ぐらいかかる)。

  • 明確なゴールを持ち、ブレない方針を持っている。
  • チームの方針について議論できる。
  • チームに個人として結果をフィードバックできる。
  • どの領域で自分が役立つか考え(バウンダリー)行動する。
  • 一瞬(0.1秒)で現状を把握して、判断して行動する。
  • チームがチームを管理する(スクラムマスターは管理者ではない)。
  • 個人は、生産性を向上する為、自ら考え努力する。

プロダクトオーナーやスクラムマスターは、そのコストを掛けてもチームに人を追加するか判断しなければなりません。

ベロシティに関して

スクラムチームは、ベロシティは測定します。
それは、スプリントで、スクラムチームがどれくらいの量の作業を行えるかわかるからです。
その情報があるとプロダクトオーナーは、そのプロダクトの見通しが立てられるし、
チームの状況を把握したり、どれだけのものを達成したか把握できるなどのメリットがあります。
スクラムマスターは、チームのベロシティが最大のところで、安定するようにしなければなりません。
チームのベロシティが最大値を基準に-2ポイントまでの範囲に収まっているなら安定していることを意味します。
また、チームメンバーがなんらかの都合で、数日いない場合でもチームは、ベロシティを下げないように考え行動しなければなりません。
スクラムマスターとチームは、ベロシティを最大のところで安定させる責任があります。
f:id:chronos_snow:20151223024429p:plain:w300

ベロシティで生産性を測れるのか?問題

ベロシティは生産性(パフォーマンス)を測る指標ではないということがSCM達の見解となっている。
また、いろいろなスクラムの書籍等にもベロシティの誤用やアンチパターンとして記述されています。
認定スクラムマスター研修でも生産性ではないとの認識。

  • ベロシティは見積のサイズで、なんらかの単位があるわけではない。
  • チームによりストーリーでつけるポイントの粒度が違う。
  • チームにより粒度が違うポイントを元に他チームと比較しても意味がない。

もし生産性を測定する必要があるなら、多くのパラメーターを取る必要がある。

  • ベロシティ
  • タスクの状態(タスク別にToDo/Doing/Doneの数)
  • Done率
  • リードタイム(機能ができるまでの時間)
  • リリース数(新機能とバグ対応のリリース数及び、その時間)

これ以外にも数値する必要なものがあるかもしれない。

スクラムマスターの重要なスキル

スクラムマスターは、技術的な知識に関しては、必須ではないが、広く浅く知識を有していることが望ましい。
深い知識はそれほど必要ではありませんが、チームの技術的な課題に関しては相談にのれる必要があります。
スクラムマスターの重要なスキルは、下記の4つ。

  • ティーチング
  • ファシリテーティング
  • メンタリング
  • コーティング

それ以外のスキル

  • シチュエーショナルリーダーシップ
  • 組織論

SpringBootでのトランザクション管理

SpringBootでのトランザクション管理の設定に関して、
トランザクション管理には、TransactionManagerとTransactionInterceptorを利用します。
Springには、PlatformTransactionManagerインタフェースを実装したTransactionManager
が何種類か用意されています。

  • org.springframework.jdbc.datasource.DataSourceTransactionManager
  • org.springframework.orm.jdo.JdoTransactionManager
  • org.springframework.orm.hibernate.HibernateTransactionManager
  • org.springframework.transaction.jta.JtaTransactionManager

通常のJDBCの場合、DataSourceTransactionManagerを使います。
このDataSourceTransactionManagerは、コンストラクターにDataSourceを渡すだけです。
次に、TransactionInterceptorを設定します。
TransactionInterceptorには、下の2つを設定する必要があります。

  • TransactionManager
  • TransactionAttributeSource

TransactionAttributeSourceですが、Propertiesでも大丈夫です。
実際の定義は、Springの設定クラスに定義します。

@Bean
public TransactionInterceptor txAdvice(DataSource dataSource) {
	DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource);
	Properties attributes = new Properties();
	attributes.setProperty("*", "PROPAGATION_REQUIRED");
	TransactionInterceptor txAdvice = new TransactionInterceptor(txManager, attributes);
	return txAdvice;
}

トランザクションの設定をしたらトランザクションを掛けるクラスを指定します。
@Transactionalをクラス又はメソッド指定することで、その部分にトランザクションを掛けます。

@Transactional
public class Logic {
}

別のやり方として、ポイントカットを利用して、トランザクションを掛けることも可能です。
SpringにDefaultPointcutAdvisorと言うポイントカットクラスがあります。
DefaultPointcutAdvisorは、下記の2つを設定します。

  • TransactionInterceptor
  • AspectJExpressionPointcut

AspectJExpressionPointcutにトランザクションを掛けたいクラスを指定します。
このクラス指定にはワーイルドカードが使えます。

@Bean
public DefaultPointcutAdvisor txAdvisor(TransactionInterceptor txAdvice) {
	DefaultPointcutAdvisor txAdvisor = new DefaultPointcutAdvisor();
	txAdvisor.setAdvice(txAdvice);
	AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
	pointcut.setExpression("execution(* chronos.domain..*(..))");
	txAdvisor.setPointcut(pointcut);
	txAdvisor.setOrder(10);
	return txAdvisor;
}

SpringBootでのインターセプターに関して

SpringBootでのインターセプターですが、
Spring MVCでのリクエストに対してのアクセスコントロールや認証などの共通処理を
実装する方法は、HandlerInterceptorを利用すると実現可能です。
これは、SpringBootでも同じですので、以前のエントリーの方法で実装は、同じです。chronosdeveloper.hatenablog.com
しかし、SpringBootだと作成したインターセプターをConfigurationのクラス内で、
登録する設定ロジックを記述してもうまく動作しない?addInterceptorsメソッドが呼ばれないようです。

@Configuration
public class WebApplicationConfiguration extends WebMvcConfigurationSupport {
    @Bean
    public SampleHandlerInterceptor sampleHandlerInterceptor() {
	return new SampleHandlerInterceptor();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(sampleHandlerInterceptor()).addPathPatterns("/**")
    }

次に確認したのは、WebMvcConfigurerAdapterを継承して、Configurationのクラスを作成。
作成したインターセプターをConfigurationのクラス内で、登録する設定ロジックを記述...
SpringBootを起動したが、これもaddInterceptorsメソッドが呼ばれない。

@Configuration
public class WebApplicationConfiguration extends WebMvcConfigurerAdapter  {
    @Bean
    public SampleHandlerInterceptor sampleHandlerInterceptor() {
	return new SampleHandlerInterceptor();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(sampleHandlerInterceptor()).addPathPatterns("/**")
    }

それで、いろいろ調べてみたら、SpringBootのドキュメントに下記の記述が!!
f:id:chronos_snow:20150706010706p:plain
Spring Boot MVCの機能を維持し、インターセプタ、フォーマット、ビュー・コントローラなどの
追加設定を行う場合は、@EnableWebMvcを使用せず、WebMvcConfigurerAdapterの
@Beanクラスを定義してくださいって書いてある!!

@Configuration
public class WebApplicationConfiguration {
    @Bean
    public SampleHandlerInterceptor sampleHandlerInterceptor() {
	return new SampleHandlerInterceptor();
    }
    @Bean
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
	return new WebMvcConfigurerAdapter() {
	    @Override
	    public void addInterceptors(InterceptorRegistry registry) {
	    	 registry.addInterceptor(sampleHandlerInterceptor()).addPathPatterns("/**");
	    }
    };
}

これも駄目だった...。
まじかwww。
applicationContext.xmlのxsdファイルを見て、MappedInterceptorを使っいるぽい?
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
Configurationのクラス内で、MappedInterceptorのBeanを定義します。

    @Bean
    public MappedInterceptor interceptor() {
        return new MappedInterceptor(new String[]{"/**"}, sampleHandlerInterceptor());
    }

このやり方ならインターセプターの登録をすることが可能で、
実際SpringBootを起動して、インターセプターがちゃんと動作することが確認できました。

SpringBootでMyBatisを利用する方法

SpringBootがORMとして、EclipseLinkなどのJPAフレームワークを使う場合は、
設定が簡単なんですが、MyBatisを利用したい場合に関しては、少し実装してあげる必要があります。
以前のエントリーの方法で、プロジェクトを作成した前提で書きます。chronosdeveloper.hatenablog.com
POMファイルに必要なものを追記します。
Springのトランザクション関連のライブラリとMyBatis関連のライブラリを追加します。

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jdbc</artifactId>
	<version>${org.springframework.version}</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-tx</artifactId>
	<version>${org.springframework.version}</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>${mybatis-spring.version}</version>
</dependency>
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>${org.mybatis.version}</version>
</dependency>	

それが終わったら、通常通りにSpringBootのアプリケーション設定クラスを作成します。

@EnableAutoConfiguration
@ComponentScan
public class ApplicationConfiguration {
}

次にsrc/main/resourcesにapplication.ymlを作成して、データーソースの設定を書きます。
SpringBootが自動的にこのファイルからデーターソースを生成してくれます。

spring:
 datasource:
  username=user
  password=pass
  url=jdbc:oracle:thin:@localhost:1521:xe
  driverClassName=oracle.jdbc.driver.OracleDriver

次にMyBatisの設定用のクラスを作成します。
このクラスにBeanの定義として、SqlSessionFactoryを生成する処理を記述します。
また、MyBatisのMapperScanはAOPの@MapperScanを使います。

@Configuration
@MapperScan(basePackages="persistence.dao") 
public class ApplicationDataSourceConfiguration {
	@Bean
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
		SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource);
		sessionFactory.setConfigLocation(new ClassPathResource("/mapper-config.xml"));
		sessionFactory.setFailFast(true);
        return sessionFactory.getObject();
	}
}

これで準備は完了です。
ちなみに、データーソースの部分に関してですが、
通常は、application.ymlに定義しておけば、自動的に生成してくれますが、
パスワードの暗号化や管理等で、なんらかのライブラリを挟む必要がある場合は、
普通にMyBatisの設定用のクラス内にデーターソースの生成処理を記述することも可能です。

@Configuration
@MapperScan(basePackages="persistence.dao") 
public class ApplicationDataSourceConfiguration {
	@Bean
	public DataSource dataSource() throws SQLException {
		oracle.jdbc.pool.OracleDataSource dataSource = new oracle.jdbc.pool.OracleDataSource();
		dataSource.setUser("datasource.username");
		dataSource.setPassword("datasource.password");
		dataSource.setURL("datasource.url");
		return dataSource;
	}
	@Bean
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
		SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource);
		sessionFactory.setConfigLocation(new ClassPathResource("/mapper-config.xml"));
		sessionFactory.setFailFast(true);
        return sessionFactory.getObject();
	}
}

MyBatis Migrations

MyBatis Migrationsの設定

Releases · mybatis/migrations · GitHubからダウンロードします。
ダウンロード後、適当なディレクトリに配置してパスを通します。
パスは、bash_profileに下記の例の様に通します。

export MYBATIS_MIGRATIONS_HOME=${HOME}/project/tools/mybatis-migrations
export PATH=${PATH}:${MYBATIS_MIGRATIONS_HOME}/bin

プロジェクトの初期化

プロジェクト用ディレクトリを作成して、下記の例の様にプロジェクトの初期化を行います。

[user@localhost]$ mkdir sample
[user@localhost]$ cd sample
[user@localhost]$ mkdir migration
[user@localhost]$ cd migration
[user@localhost]$ migrate init
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
------------------------------------------------------------------------
-- MyBatis Migrations - init
------------------------------------------------------------------------
Initializing: .
Creating: environments
Creating: scripts
Creating: drivers
Creating: README
Creating: development.properties
Creating: bootstrap.sql
Creating: 20150413014605_create_changelog.sql
Creating: 20150413014606_first_migration.sql
Done!
------------------------------------------------------------------------
-- MyBatis Migrations SUCCESS
-- Total time: 2s
-- Finished at: Mon Apr 13 10:46:06 JST 2015
-- Final Memory: 10M/479M
------------------------------------------------------------------------

作成したプロジェクトのディレクトリ構成は、下記の内容になっています。

<project>
  |-migration
       |-drivers ← JDBCドライバー配置場所
       |-environments ← DB接続設定ファイル配置場所
       |-scripts ← SQLファイル配置場所

マイグレーションを行うDBのJDBCドライバーをmigration/driversへ配置します。

DB接続設定

migration/environments/development.propertiesにDBへの接続情報を定義します。

time_zone=GMT+0:00
driver=<JDBCドライバークラス名(Oracleの場合、oracle.jdbc.driver.OracleDriver)>
url=<JDBC接続URL(Oracleの場合、jdbc:oracle:thin:@localhost:1521:xe>)
username=<ユーザー名>
password=<パスワード>
send_full_script=false
delimiter=;
full_line_delimiter=false
auto_commit=false
changelog=CHANGELOG

このファイルは、環境ごとに用意する必要があります。
その為、例えば開発、ステージ、本番を用意する場合は、下記の3つファイルを用意します。

  • development.properties(開発)
  • staging.properties(ステージ)
  • production.properties(本番)

Scriptsファイル作成

下記のコマンドでSQLファイルを生成します。

[user@localhost]$ migrate new create_hello_table
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
------------------------------------------------------------------------
-- MyBatis Migrations - new
------------------------------------------------------------------------
Creating: 20150413050113_create_hello_table.sql
Done!
------------------------------------------------------------------------
-- MyBatis Migrations SUCCESS
-- Total time: 1s
-- Finished at: Mon Apr 13 14:01:13 JST 2015
-- Final Memory: 7M/479M
------------------------------------------------------------------------

migration/scriptsにテンプレートファイルが作成されるので、そのファイルにDBへの更新用SQLと更新を戻す用SQLを定義します。

-- // create_hello_table
-- Migration SQL that makes the change goes here.
CREATE TABLE HELLO_TABLE
(
    MESSAGE varchar2(50) NOT NULL
);
-- //@UNDO
-- SQL to undo the change goes here.
DROP TABLE HELLO_TABLE CASCADE CONSTRAINTS;

操作方法

[user@localhost]$ migrate up ← DBを更新する場合
[user@localhost]$ migrate down ← DBを戻す場合

Bootstrapに関して

途中からこのツールを導入する場合、既に作成済みのテーブルのSQLをmigration/scripts/bootstrap.sqlに全て定義します。
途中で、開発メンバーが入ったり、DBを作り直したりする場合は、下記のコマンドを実行後、今まで当てたパッチをDBへ反映します。

[user@localhost]$ migrate bootstrap --force
[user@localhost]$ migrate up

SpringBootの初め方

SpringFrameworkですが、コアプロダクト以外にも関連プロダクトが多く、
どれを選択するかは、かなり慣れないと大変です。
また設定ファイルも多くなったりして記述量が多く、管理も大変だったりします。
SpringBootを使えばかなり楽になる気がします。
SPringBootの初め方ですが、まずは、Mavenプロジェクトを作成します。
作るプロジェクトのタイプはなんでもいいと思いますが、
クイックスタートを使って作ることにします。

mvn archetype:generate -DgroupId=sample -DartifactId=sample -DarchetypeArtifactId=maven-archetype-quickstart

生成されたプロジェクトのPOMへSpringBootの設定を追加します。
親プロジェクトとして、SpringBootの物があるので、それを指定します。

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.2.2.RELEASE</version>
</parent>

次に必要な依存関係を定義します。
これもSpringBoot用のものがあるのでそれを指定します。
最低限必要なものは、下記の4つを指定するだけで良いはずです。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>

ちなみにSpringBootのデフォルトの組み込みサーバーはTomcatなので、
Jettyを利用したい場合は、追加の設定が必要で、下記の設定を追加する必要があります。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

SpringMVCでRestでJSONを扱いたい場合は、デフォルトのままだと利用出来ないので、
JSONライブラリを追加で設定する必要があります。

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-core</artifactId>
	<version>${jackson.version}</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>${jackson.version}</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-annotations</artifactId>
	<version>${jackson.version}</version><!--$NO-MVN-MAN-VER$-->
</dependency>

SpringBootの設定は、以上です。
これで、実際SpringBootで作って行けるようになると思います。
SpringBootは、まずアプリケーション設定クラスを作成します。
ApplicationContext.xmlの代わりみたいなもので、
基本的にいままで、ApplicationContext.xmlの定義した内容は、
SpringBootの場合は、このクラスに定義して行きます。

@EnableAutoConfiguration
@ComponentScan
public class ApplicationConfiguration {
}

@EnableAutoConfigurationのアノテーションが自動設定してくれます。
このアノテーションを付けておくと、SpringBootにあるデフォルトの設定ファイルを
自動的にロードしてくれます。
@ComponentScanのアノテーションは、このアノテーションを付けたクラスがある
パッケージ配下を全てスキャンして、コンテナに登録してくれます。
次に起動用のクラスを作成します。
Mainメソッド内で、起動定義を記述します。

@SpringBootApplication
public class JettyApplication {
	/**
	 * 起動処理
	 * 
	 * @param args コマンドライン引数
	 */
	public static void main(String[] args) {
		// Jetty起動
		SpringApplication.run(ApplicationConfiguration.class, args);
	}
}

SpringBootのアプリケーションであることを明示的に書かないといけないので、
@SpringBootApplicationのアノテーションを付ける必要があります。
最後にコントローラークラスを作成すれば完了です。

@RequestMapping(value="/hello")
@RestController
public class HelloService {
	/**
	 * Helloメッセージ
	 * 
	 * @return helloメッセージ
	 */
	@RequestMapping(value = "/message", method = RequestMethod.GET)
	public String message() {
		return "HELLO";
	}
}

起動方法ですが、Eclipseを使っている場合は、Mainクラスを起動するだけで、起動します。

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.2.RELEASE)

2015-04-27 00:25:50.678  INFO 1979 --- [           main] j.c.y.s.developer.app.JettyApplication   : Starting JettyApplication on MBP-15UAC-218.local with PID 1979 (/Users/toirie/project/workspace/corpit_spring_boot/target/classes started by toirie in /Users/toirie/project/workspace/corpit_spring_boot)
2015-04-27 00:25:50.712  INFO 1979 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4d3167f4: startup date [Mon Apr 27 00:25:50 JST 2015]; root of context hierarchy
2015-04-27 00:25:51.184  WARN 1979 --- [           main] o.m.s.mapper.ClassPathMapperScanner      : No MyBatis mapper was found in '[jp.co.yahoo.system.developer.persistence]' package. Please check your configuration.
2015-04-27 00:25:51.237  INFO 1979 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2015-04-27 00:25:51.521  INFO 1979 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [class org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$2c77d480] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2015-04-27 00:25:51.534  INFO 1979 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'transactionAttributeSource' of type [class org.springframework.transaction.annotation.AnnotationTransactionAttributeSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2015-04-27 00:25:51.540  INFO 1979 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'transactionInterceptor' of type [class org.springframework.transaction.interceptor.TransactionInterceptor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2015-04-27 00:25:51.544  INFO 1979 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.config.internalTransactionAdvisor' of type [class org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2015-04-27 00:25:51.700  INFO 1979 --- [           main] e.j.JettyEmbeddedServletContainerFactory : Server initialized with port: 8080
2015-04-27 00:25:51.703  INFO 1979 --- [           main] org.eclipse.jetty.server.Server          : jetty-9.2.9.v20150224

これで簡単なアプリとかは作れるようになるかと...。
データーベース周りとかは、また今度、書こうと思います。