阪田 浩一 (さかた こういち)
@jyukutyo じゅくちょー
フリュー株式会社
元塾講師アルバイト
関西Javaエンジニアの会(関ジャバ) 会長!?
2009年秋 発足
2014年11月 JUG入り
SI(客先常駐) 9年 / 自社サービ...
2016/9/26にSeasar2は
サポートを終了する!
DBFlute DBFlute.NET Doma
Emecha Mayaa S2Container.NE
T
S2Dao.NET
以下を除いた全プロダクトが
EOLとなります!
つまりJavaでの
Seasar2関連は
ほとんどが対象
このセッションは、
それを受けて
プロジェクトで
移行を始めた
事例の紹介です
今日のゴール
対象 S2で開発/運用している人
提供したい
こと
対象の人たちがS2から
移行する際の参考事例
注意事項
“ベスト”プラクティスでは
ないこと
まずは結論から
結果:Springに移行
2016年2月にリリース
移行前 移行後
プレゼンテー
ション層 Cubby 2.0
Spring MVC
2.4.3
永続化層 S2Dao 1.0.51 Doma 2.6.0
コンポーネント
フレームワーク S2 2...
ただし、2月に
リリースしたのは
あくまで1機能のみ
リリース内容は
同一機能の
リプレース
その後
さらにいくつか
リプレースした
2016年5月現在
3機能がSpringで
動作している
そんな状況です
今日
お話しすること
結果に至るまでのこと
“なぜ”Spring/Domaなのか?
“どのようにして”移行したのか?
“何に”詰まった/困ったのか?
なぜSpring/Doma
なのか?
WebSocketやSSEを(楽に)使いたい
Java 8に移行したので、
それに対応するFWにしたい
長期間、同じ運用開発をしているので、
メンバーの飽きを防ぎたい
ただし移行の緊急性は高くない
(ように思う)
そもそもS2から移行する
必要...
こうした理由に至った
僕らの背景を
簡単にご紹介します
(あくまで理解の
補助として)
対象アプリケーション
サービス名 ピクトリンク
サービス
イン
2011年
(前身サービスはもっと以前から)
利用者数
1,000万人
女子高生の70%は会員
(有料会員数は秘密)
主な機能
プリントシール機で撮影した画像を
取得できる
提供
...
備考:プリントシール機とは
ゲームセンターや
ショッピングモールにある
写真を撮影をする機械。
女性の利用が多い。
開発チーム構成
サイトチーム 6名
• 私がリーダを担当
アプリチーム 7名
• iOS
• Android
• Web API
インフラチーム 5名
• サーバ
• ネットワーク
• Ansible
• Elasticsearch/Ki
ba...
合計23名
私はサービスインの
半年後に
参画しました
23名という人数と
開発開始から
5年以上経過している、
そういう状況
10年くらい標準だったよう
S2、S2Struts、Cubby、S2Daoなど
ただし、ここ5年ほど私の部署以外では
新規プロジェクトでScalaを採用
全社的に標準が
S2ファミリーだった
言語はJavaとする
運用開発チームの特性上、FWだけでなく
言語も変わると、メンバーが対応できない
日々の工数すべてを移行に避けない
機能改善/追加と並行に作業する
アプリケーションを作り直さない
テスト手順が煩雑なキャリア課金がメインのため...
All or Nothingの移行は
やっぱり
リスクが高すぎる!
(ように思う)
まずは移行先となる
フレームワーク(FW)
を調査する
移行先FWで調査した候補
Spring
Spring MVC
Spring Boot
Java EE
JSF
JAX-RS
Jersey MVC
Play Framework
Spark Framework
Jodd
Ninja
Doma
JO...
考えたこと:
もうトレードオフで
判断するしかない
S2、Cubby、S2Daoとそれほど
考え方が離れていないFWとする
学習にかけられる期間、工数を最小限に
今あるサービスクラスを
(少なくとも多少コードを書けば)
利用できるFWとする
トレードオフで重視したこと
Servletで動作するFWとする
既存のWebアプリと同一のWARファイルに
監視会社へ監視依頼する数が増えない
コストが増えない
同一のアプリケーションとなりシンプル
アプリケーション間で連携するのは複雑そう
トレードオフで重視したこと
みなさんの
プロジェクトでは、
どんな前提条件や
どんなことを
重視しますか?
そして、
僕らの判断理由は
決定:Spring MVC + Doma!
プレゼンテー
ション層
Spring MVC
2.4.3
Cubbyのアノテーションの
使い方と似てる感じ。
WebSocketに対応。
永続化層
Doma
2.6.0
S2Daoと同じく2-Way ...
ちょっと詳細
Cubby ->
Spring MVC
CubbyとSpring MVC似てる?
Cubby
@Accept(RequestMethod.GET)
@Path("index")
public ActionResult index() {
return new Forward("/in...
Cubbyから移る
前提なら、
読めば内容が
理解できる近さ
S2Dao ->
Doma 2
S2DaoとDomaのDaoクラス
S2Dao
@S2Dao(bean = Employee.class)
public interface EmployeeDao {
@SqlFile
@Arguments({ ”employeeId” })...
S2DaoとDomaのDaoクラス
Doma
@Dao
@AnnotateWith(annotations = {
// 生成されたDAO実装クラスに@Componentを付与する
@Annotation(target = Annotation...
S2DaoとDomaの
SQLテンプレート
S2Dao
select
*
from
employee
where
/*IF employeeId != null */
employee_id = /*employeeId*/9999
-- EL...
S2DaoとDomaの
SQLテンプレート
Doma
select
*
from
employee
where
/*%if employeeId != null */
employee_id = /* employeeId */9999
/*%...
差異は少ない
Domaの
公式ドキュメントに
DIコンテナとの
連携方法も
書いてある
どのようにして
移行したのか?
まず
2014〜15年で
Javaを 6 -> 8
Tomcatを 6 -> 8
へ移行しておいた
基盤のバージョンアップは
すでにクリアしている
ただし、Java 8の機能を
駆使したコードは
まだなかった
同時に当時から
少しずつ時間を取り、
メンバーに
Java 8の機能をレクチャー
新FWの学習だけを
進めればよい状態
としていた
さて、
移行後
アプリケーションは
こういう構造に
Application (WAR)
S2Servlet DispatcherServlet
DIコンテナ DIコンテナ
Cubby Action
Service / Logic
S2Dao DAO
Interceptor
MVC Control...
1つのアプリケーション
2つのDIコンテナ
DIコンテナそれぞれで
重複して
オブジェクトを
管理することになる
なお、サーバ起動時間、
ヒープ使用量に
大きな差は出ていない
(だいたい
シングルトンなので)
新旧FWの振り分け
Servlet Mapping / Filter
/*
/2/* 左記以外
Cubby ActionMVC Controller
web.xmlで設定
続いて
既存資産の利用
S2Daoを利用している
Service / Logicを
既存資産として
Springでも利用する
つまり、S2Daoの
Daoオブジェクトを
SpringのBeanとして
管理できればよい!
SpringでS2Daoを使う -その1-
n-ichimuraの日記
2006年!
http://d.hatena.ne.jp/n-ichimura/20061119/1163944881
過去にSpring + S2Daoを
検証した方が!
そのコードを参考に
現在の状況に合わせて
実装した
これで無事
SpringでS2Daoが
利用できた!
続いて
Spring MVCでの
課題
Cubby Actionから
Spring MVC Controller
へ書き換えるのは
いいとして
リクエストパラメータ
のバリデーションは
どうしよう?
Cubby標準のバリデーションから
Spring MVCでBean Validation利用へ
JSR-349 Bean Validation 1.1
Method Validationを使う
コントローラのメソッドを対象に
リクエストパラメー...
なぜ
Method Validation
を選んだのか?
Cubbyからの移行なので、
リクエストパラメータ
をModelに入れる
方式に慣れていない
(メンバーが多い)
サービスの性質上、
複雑なバリデーション
はない
メソッドの引数に
アノテーションを
つけるだけの
Method Validation
でよさそう
という判断です
Cubbyでのバリデーション
アクションクラス
@RequestParameter(name = ”hoge”)
public String hoge;
protected ValidationRules rule =
new DefaultV...
リクエストパラメータ
を1つずつ
インスタンス変数で
受け取っていた
メソッドバリデーション
コントローラクラス
@Controller
@Validated
public class HogeController {
@RequestMapping(path = “validate",
method = Req...
Spring MVCでは
リクエストパラメータを
コントローラメソッドの
引数で受け取れる
この引数に対して
アノテーションで
バリデーションを
設定する
メソッドバリデーション
コントローラクラス
@Controller
@Validated
public class HogeController {
@RequestMapping(path = “validate",
method = Req...
Bean Validation仕様と
その実装Hibernate
Validator独自の
バリデーションの
アノテーションは
そこそこ用意されている
バリデーション用
アノテーション
@AssertFalse/True
@DecimalMax/Min
@Digits
@Future/Past
@Max/Min
@Null/NotNull
@Pattern
@Size
@CreditCardN...
独自のアノテーションも
もちろん作れる
で、
バリデーションエラーに
なったときは?
例外
javax.validation.
ConstraintViolationException
が発生する
この例外を
Springの
ExceptionHandlerで
処理することにする
ExceptionHandler
@ExceptionHandler(value=ConstraintViolationException.class)
public ModelAndView handleValidationFailure(
...
これでほぼOKなんだけど
困ったことが…
最初SpringでMethod Validationが
動作せずちょっとハマった
Bean Validationはメッセージリソース
を利用できるが、”{0}は必須です”の
{数字}の置換が使えない
困ったこと
SpringでMethod
Validationを使う時は
MethodValidationPostProcessor
をBean登録する、
のだけど…
MethodValidationPostProcessor
インスタンスの生成時に
LocalValidatorFactoryBean
インスタンスをvalidatorとして
セットしましょう
公式リファレンスには
はっきりそうと書いていない感...
MethodValidationの
Bean設定
@Bean public LocalValidatorFactoryBean l() {
LocalValidatorFactoryBean l = new LocalValidatorFact...
Bean Validatorでメッセージに
リソースバンドルの値は設定できる
@NotEmpty(message=“{valid.required}”)
Validationのアノテーションの属性値
はリソースバンドルから読める
@Size(m...
今使っているこういうのができなさそう
required={0}は必須です
password=パスワード
name=名前
組み合わせて...
“パスワードは必須です”、“名前は必須です”
Bean Validationでの
リソースバンドルの利用
org.hibernate.validator.messageinterpolation.
ResourceBundleMessageInterpolator
を継承して実装した
length={0}は{1}文字以下です
password=パス...
難点:プロパティのキーを
複数使ったり、キー文字列
が長いと読みづらい
message = "{valid.maxLength}
{0:hoge.fuga.example.title}{1:max}"
よりよい方法があれば
ぜひ教えてください〜
メッセージリソースは
そのままSpring MVCでも
使えるようになった
ここでまったく
別の観点で課題が
既存のユニットテスト
が動かない!
全クラスにユニットテストがある
S2TigerTestCaseクラスを継承
EasyMock + djUnit + JUnit3
ユニットテスト
ラムダ式を書いたら
djUnitの部分で
エラーが発生!
java.lang.Error: djUnit class load error
(Class : com.example.ExampleClass)
at jp.co.dgic.testing.common.DJUnitClassLoader...
djUnitはもう
更新がないため、
Java 8に対応していない
内部で利用している
ASM/BCELの
メジャーバージョンアップ
に対応していない
逆に言うと、
djUnitを使っていない
ユニットテストなら
動作する
新規/変更したクラスのユニットテスト
は、クラス単位ですべて書き換えた
JMockit + JUnit4
ユニットテストの書き換え
モックライブラリ
機能が豊富
APIの呼び出し方が少し変わっている
JMockitとは
JMockit or Mockito + PowerMockで検討
JMockitの方が、djUnitを使ったテストコー
ドを単純に書き換えやすそうに感じた
JMockitにした理由
JMockitへの書き換え
S2TigerTestCase + EasyMock + djUnit
public class ExampleActionTest
extends S2TigerTestCase {
private Example...
JMockitへの書き換え
JMockit
public class ExampleControllerTest {
@Tested
private ExampleController tester;
@Injectable
private E...
JMockitでの書き方が
見慣れない感じですね
しかし
チームへの導入で
とくに抵抗はなく、
みな書き換えができた
ちなみに、
移行した機能の
いわゆる結合テストは
すべて手で再実施…
後の方で
発覚したこと
「あ、S2Velocityの
機能も移行しないと
いけないのか」
S2Velocityを使い、Veloctityツールの
インスタンスをS2Containerで
管理していた
Velocityツールとは
テンプレートファイルで利用する
ユーティリティクラス
Velocityの利用
同一のテンプレートファイルを
Springのときでも動作させるためには
同様の仕組みが必要となる
ToolboxManager/VelocityToolboxView
を実装
SpringのコンテナからVelocityContextに
Velo...
これで既存の
テンプレートファイルを
そのままSpring MVCから
利用できるようになった
Tipsを2点
Tips(1)
既存クラス
@Component(name = ”example”,
instance = InstanceType.REQUEST)
public class ExampleClass {
スコープの設定
@Component...
Tips(2)
既存クラス
@Component(name = ”example”,
instance = InstanceType.REQUEST)
public class ExampleClass {
Bean名の設定
@Componen...
その他
2時間のミーティング
主に以前との差分、新しい書き方や
意味を説明
ほぼ抵抗なく習得へ進んだ
メンバーへのレクチャー
Springで動作させる機能を増やす
2016年5月現在で3機能なので
機能単位での独立したサービスにする
マイクロサービス化?
企画要件へ利用できるようになった
技術を適用する
WebSocketを使うとユーザにどんな体験を
提供できるか
今...
S2からの移行、
怖くないよ!
ご清聴
ありがとうございました!
Q&A
Seasar2で作った俺たちのサービスの今
Upcoming SlideShare
Loading in …5
×

Seasar2で作った俺たちのサービスの今

524 views
529 views

Published on

2016/05/21(土) 17:00〜17:50 JJUG CCC 2016 Spring GH-6

2011年にリリースした弊社のサービスは、Seasar2ファミリーで構築しました。利用者数は1000万人以上となり、今もサービスとして成長しています。2016/9/26にSeasar2がサポートを終了するというアナウンスを受け、私たちもアクションを起こしました。

サービスには絶え間なく機能改善、機能追加の要件があり、その対応をしながらSeasar2から移行するという前提条件を考慮すると、選択肢は限られます。Scalaなど言語自体を変えることはなく、Javaのままとしました。新規アプリケーションを作成すると監視対象とするアプリケーションの数が増え、様々なコストが増えると考えたため新規とはしませんでした。こういったさまざまな条件のもと、まずはSeasar2で動作している機能から、まずリスクが小さい機能を新フレームワークで置き換え(ユニットテストも書き換え受け入れテストもし直し)てリリースしました。そして機能追加と並行して置き換えを進めています。

技術的には、移行対象としてはSpringを選択しました。Seasar2にある機能がそのままある場合もあれば、コードを書いて対応したものもあります。他に、S2DaoでのRDBMSへのアクセスを含んだビジネスロジックも、移行コストを最低限にするために資産としてSpringのコードからも利用できるようにしました。ユニットテストについてもS2TestCase とdjUnitから、単純作業として書き換えやすかったjMockitに移行しました。

セッションでは、こういった判断に至った経緯やその実現手法、メンバーへレクチャーなど移行のスタートから現時点のリリースまでにあるさまざまなことを話します。すばらしい判断でもなければすごい技術力で解決したわけでもない、普通なプロジェクトの現場で実際にやったことならではのリアルさを感じていただけると思います。

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
524
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Seasar2で作った俺たちのサービスの今

  1. 1. 阪田 浩一 (さかた こういち) @jyukutyo じゅくちょー フリュー株式会社 元塾講師アルバイト 関西Javaエンジニアの会(関ジャバ) 会長!? 2009年秋 発足 2014年11月 JUG入り SI(客先常駐) 9年 / 自社サービス 5年目 blog : Fight the Future http://jyukutyo.hatenablog.com 昨年(2015年)のJJUG CCC 2015 Springでも話しました http://www.slideshare.net/jyukutyo/jjug-ccc-2015ab4
  2. 2. 2016/9/26にSeasar2は サポートを終了する! DBFlute DBFlute.NET Doma Emecha Mayaa S2Container.NE T S2Dao.NET 以下を除いた全プロダクトが EOLとなります!
  3. 3. つまりJavaでの Seasar2関連は ほとんどが対象
  4. 4. このセッションは、 それを受けて プロジェクトで 移行を始めた 事例の紹介です
  5. 5. 今日のゴール 対象 S2で開発/運用している人 提供したい こと 対象の人たちがS2から 移行する際の参考事例 注意事項 “ベスト”プラクティスでは ないこと
  6. 6. まずは結論から
  7. 7. 結果:Springに移行 2016年2月にリリース 移行前 移行後 プレゼンテー ション層 Cubby 2.0 Spring MVC 2.4.3 永続化層 S2Dao 1.0.51 Doma 2.6.0 コンポーネント フレームワーク S2 2.4.43 Spring 2.4.3
  8. 8. ただし、2月に リリースしたのは あくまで1機能のみ
  9. 9. リリース内容は 同一機能の リプレース
  10. 10. その後 さらにいくつか リプレースした
  11. 11. 2016年5月現在 3機能がSpringで 動作している
  12. 12. そんな状況です
  13. 13. 今日 お話しすること
  14. 14. 結果に至るまでのこと “なぜ”Spring/Domaなのか? “どのようにして”移行したのか? “何に”詰まった/困ったのか?
  15. 15. なぜSpring/Doma なのか?
  16. 16. WebSocketやSSEを(楽に)使いたい Java 8に移行したので、 それに対応するFWにしたい 長期間、同じ運用開発をしているので、 メンバーの飽きを防ぎたい ただし移行の緊急性は高くない (ように思う) そもそもS2から移行する 必要があるのか?
  17. 17. こうした理由に至った 僕らの背景を 簡単にご紹介します (あくまで理解の 補助として)
  18. 18. 対象アプリケーション サービス名 ピクトリンク サービス イン 2011年 (前身サービスはもっと以前から) 利用者数 1,000万人 女子高生の70%は会員 (有料会員数は秘密) 主な機能 プリントシール機で撮影した画像を 取得できる 提供 Webサイト iOSアプリ/Androidアプリ 課金 携帯電話キャリア課金 / 楽天ID決済 iOS課金 / Android課金 PageView 月間約1億PV
  19. 19. 備考:プリントシール機とは ゲームセンターや ショッピングモールにある 写真を撮影をする機械。 女性の利用が多い。
  20. 20. 開発チーム構成 サイトチーム 6名 • 私がリーダを担当 アプリチーム 7名 • iOS • Android • Web API インフラチーム 5名 • サーバ • ネットワーク • Ansible • Elasticsearch/Ki bana • Oracle RAC ログチーム 3名 • Tableau • BigQuery HTML/CSS 2名 • デザインをHTML に
  21. 21. 合計23名
  22. 22. 私はサービスインの 半年後に 参画しました
  23. 23. 23名という人数と 開発開始から 5年以上経過している、 そういう状況
  24. 24. 10年くらい標準だったよう S2、S2Struts、Cubby、S2Daoなど ただし、ここ5年ほど私の部署以外では 新規プロジェクトでScalaを採用 全社的に標準が S2ファミリーだった
  25. 25. 言語はJavaとする 運用開発チームの特性上、FWだけでなく 言語も変わると、メンバーが対応できない 日々の工数すべてを移行に避けない 機能改善/追加と並行に作業する アプリケーションを作り直さない テスト手順が煩雑なキャリア課金がメインのため 移行先FWの前提条件
  26. 26. All or Nothingの移行は やっぱり リスクが高すぎる! (ように思う)
  27. 27. まずは移行先となる フレームワーク(FW) を調査する
  28. 28. 移行先FWで調査した候補 Spring Spring MVC Spring Boot Java EE JSF JAX-RS Jersey MVC Play Framework Spark Framework Jodd Ninja Doma JOOQ JDBI Bootiful SQL Template A simple SQL template engine for Spring Boot
  29. 29. 考えたこと: もうトレードオフで 判断するしかない
  30. 30. S2、Cubby、S2Daoとそれほど 考え方が離れていないFWとする 学習にかけられる期間、工数を最小限に 今あるサービスクラスを (少なくとも多少コードを書けば) 利用できるFWとする トレードオフで重視したこと
  31. 31. Servletで動作するFWとする 既存のWebアプリと同一のWARファイルに 監視会社へ監視依頼する数が増えない コストが増えない 同一のアプリケーションとなりシンプル アプリケーション間で連携するのは複雑そう トレードオフで重視したこと
  32. 32. みなさんの プロジェクトでは、 どんな前提条件や どんなことを 重視しますか?
  33. 33. そして、 僕らの判断理由は
  34. 34. 決定:Spring MVC + Doma! プレゼンテー ション層 Spring MVC 2.4.3 Cubbyのアノテーションの 使い方と似てる感じ。 WebSocketに対応。 永続化層 Doma 2.6.0 S2Daoと同じく2-Way SQL。 Java 8に対応。 新規DaoはDomaで、既存 Daoは少しずつ置き換え。 コンポーネント フレームワーク Spring 2.4.3 S2Daoを含んだ既存資産は DIコンテナがある方が 利用しやすいと判断。
  35. 35. ちょっと詳細
  36. 36. Cubby -> Spring MVC
  37. 37. CubbyとSpring MVC似てる? Cubby @Accept(RequestMethod.GET) @Path("index") public ActionResult index() { return new Forward("/index.vm"); } Spring MVC @RequestMapping(path = "/index", method = RequestMethod.GET) public String index() { return "/index.vm"; }
  38. 38. Cubbyから移る 前提なら、 読めば内容が 理解できる近さ
  39. 39. S2Dao -> Doma 2
  40. 40. S2DaoとDomaのDaoクラス S2Dao @S2Dao(bean = Employee.class) public interface EmployeeDao { @SqlFile @Arguments({ ”employeeId” }) Employee findById(Integer employeeId); }
  41. 41. S2DaoとDomaのDaoクラス Doma @Dao @AnnotateWith(annotations = { // 生成されたDAO実装クラスに@Componentを付与する @Annotation(target = AnnotationTarget.CLASS, type = Component.class), // 生成されたDAO実装クラスのコンストラクタに // @Autowiredを付与する @Annotation(target = AnnotationTarget.CONSTRUCTOR, type = Autowired.class)}) public interface EmployeeDao { @Select Employee selectById(Integer employeeId); }
  42. 42. S2DaoとDomaの SQLテンプレート S2Dao select * from employee where /*IF employeeId != null */ employee_id = /*employeeId*/9999 -- ELSE department_id is null /*end*/
  43. 43. S2DaoとDomaの SQLテンプレート Doma select * from employee where /*%if employeeId != null */ employee_id = /* employeeId */9999 /*%else*/ department_id is null /*%end*/
  44. 44. 差異は少ない
  45. 45. Domaの 公式ドキュメントに DIコンテナとの 連携方法も 書いてある
  46. 46. どのようにして 移行したのか?
  47. 47. まず
  48. 48. 2014〜15年で Javaを 6 -> 8 Tomcatを 6 -> 8 へ移行しておいた
  49. 49. 基盤のバージョンアップは すでにクリアしている
  50. 50. ただし、Java 8の機能を 駆使したコードは まだなかった
  51. 51. 同時に当時から 少しずつ時間を取り、 メンバーに Java 8の機能をレクチャー
  52. 52. 新FWの学習だけを 進めればよい状態 としていた
  53. 53. さて、
  54. 54. 移行後 アプリケーションは こういう構造に
  55. 55. Application (WAR) S2Servlet DispatcherServlet DIコンテナ DIコンテナ Cubby Action Service / Logic S2Dao DAO Interceptor MVC Controller Service / Logic S2Dao DAO Interceptor Doma2 DAO 同一のJS/CSS/テンプレートファイル
  56. 56. 1つのアプリケーション 2つのDIコンテナ
  57. 57. DIコンテナそれぞれで 重複して オブジェクトを 管理することになる
  58. 58. なお、サーバ起動時間、 ヒープ使用量に 大きな差は出ていない
  59. 59. (だいたい シングルトンなので)
  60. 60. 新旧FWの振り分け Servlet Mapping / Filter /* /2/* 左記以外 Cubby ActionMVC Controller web.xmlで設定
  61. 61. 続いて 既存資産の利用
  62. 62. S2Daoを利用している Service / Logicを 既存資産として Springでも利用する
  63. 63. つまり、S2Daoの Daoオブジェクトを SpringのBeanとして 管理できればよい!
  64. 64. SpringでS2Daoを使う -その1- n-ichimuraの日記 2006年! http://d.hatena.ne.jp/n-ichimura/20061119/1163944881 過去にSpring + S2Daoを 検証した方が!
  65. 65. そのコードを参考に 現在の状況に合わせて 実装した
  66. 66. これで無事 SpringでS2Daoが 利用できた!
  67. 67. 続いて Spring MVCでの 課題
  68. 68. Cubby Actionから Spring MVC Controller へ書き換えるのは いいとして
  69. 69. リクエストパラメータ のバリデーションは どうしよう?
  70. 70. Cubby標準のバリデーションから Spring MVCでBean Validation利用へ JSR-349 Bean Validation 1.1 Method Validationを使う コントローラのメソッドを対象に リクエストパラメータの バリデーション
  71. 71. なぜ Method Validation を選んだのか?
  72. 72. Cubbyからの移行なので、 リクエストパラメータ をModelに入れる 方式に慣れていない (メンバーが多い)
  73. 73. サービスの性質上、 複雑なバリデーション はない
  74. 74. メソッドの引数に アノテーションを つけるだけの Method Validation でよさそう
  75. 75. という判断です
  76. 76. Cubbyでのバリデーション アクションクラス @RequestParameter(name = ”hoge”) public String hoge; protected ValidationRules rule = new DefaultValidationRules() { @Override protected void initialize() { add(”hoge”, new RequiredValidator()); } }; @Validation(rules = ”rule”, errorPage = ”/error.vm”) public ActionResult index() {
  77. 77. リクエストパラメータ を1つずつ インスタンス変数で 受け取っていた
  78. 78. メソッドバリデーション コントローラクラス @Controller @Validated public class HogeController { @RequestMapping(path = “validate", method = RequestMethod.POST) public ModelAndView validate(@NotEmpty String s) { .. }
  79. 79. Spring MVCでは リクエストパラメータを コントローラメソッドの 引数で受け取れる
  80. 80. この引数に対して アノテーションで バリデーションを 設定する
  81. 81. メソッドバリデーション コントローラクラス @Controller @Validated public class HogeController { @RequestMapping(path = “validate", method = RequestMethod.POST) public ModelAndView validate(@NotEmpty String s) { .. }
  82. 82. Bean Validation仕様と その実装Hibernate Validator独自の バリデーションの アノテーションは そこそこ用意されている
  83. 83. バリデーション用 アノテーション @AssertFalse/True @DecimalMax/Min @Digits @Future/Past @Max/Min @Null/NotNull @Pattern @Size @CreditCardNumber @Email @NotBlank @NotEmpty @Range @URL @ScriptAssert etc.
  84. 84. 独自のアノテーションも もちろん作れる
  85. 85. で、
  86. 86. バリデーションエラーに なったときは?
  87. 87. 例外 javax.validation. ConstraintViolationException が発生する
  88. 88. この例外を Springの ExceptionHandlerで 処理することにする
  89. 89. ExceptionHandler @ExceptionHandler(value=ConstraintViolationException.class) public ModelAndView handleValidationFailure( ConstraintViolationException e) { Set<ConstraintViolation<?>> v = e.getConstraintViolations(); ... // Cubbyにおけるバリデーション失敗時のJSON構造と合わせる. // バリデーション対象の変数名をキー、メッセージのリストを値としたMapに Map<String, List<String>> f = v.stream() .collect( toMap( c -> ((PathImpl)c.getPropertyPath()).getLeafNode().asString(), c -> { List<String> l = new ArrayList<>(1); l.add(c.getMessage()); return l; }
  90. 90. これでほぼOKなんだけど 困ったことが…
  91. 91. 最初SpringでMethod Validationが 動作せずちょっとハマった Bean Validationはメッセージリソース を利用できるが、”{0}は必須です”の {数字}の置換が使えない 困ったこと
  92. 92. SpringでMethod Validationを使う時は MethodValidationPostProcessor をBean登録する、 のだけど…
  93. 93. MethodValidationPostProcessor インスタンスの生成時に LocalValidatorFactoryBean インスタンスをvalidatorとして セットしましょう 公式リファレンスには はっきりそうと書いていない感じ SpringでMethod Validation が動作せずちょっとハマった
  94. 94. MethodValidationの Bean設定 @Bean public LocalValidatorFactoryBean l() { LocalValidatorFactoryBean l = new LocalValidatorFactoryBe... ReloadableResourceBundleMessageSource r = new ReloadableResourceBundleMessageSource (); r.setBasename("classpath:/messages"); l.setValidationMessageSource(r); return l; } @Bean public MethodValidationPostProcessor m(LocalValidatorFactoryBean l) { MethodValidationPostProcessor m = new MethodValidationPostProcessor(); m.setValidator(l); return m; }
  95. 95. Bean Validatorでメッセージに リソースバンドルの値は設定できる @NotEmpty(message=“{valid.required}”) Validationのアノテーションの属性値 はリソースバンドルから読める @Size(min = 2, message = ”{size.min}”) size.min={min}桁未満は入力できません Bean Validationでの リソースバンドルの利用
  96. 96. 今使っているこういうのができなさそう required={0}は必須です password=パスワード name=名前 組み合わせて... “パスワードは必須です”、“名前は必須です” Bean Validationでの リソースバンドルの利用
  97. 97. org.hibernate.validator.messageinterpolation. ResourceBundleMessageInterpolator を継承して実装した length={0}は{1}文字以下です password=パスワード five=5 @Size(message = ”{length}{0:password}{1:five}”) 出力:パスワードは5文字以下です 組み合わせられる実装を 作った
  98. 98. 難点:プロパティのキーを 複数使ったり、キー文字列 が長いと読みづらい message = "{valid.maxLength} {0:hoge.fuga.example.title}{1:max}"
  99. 99. よりよい方法があれば ぜひ教えてください〜
  100. 100. メッセージリソースは そのままSpring MVCでも 使えるようになった
  101. 101. ここでまったく 別の観点で課題が
  102. 102. 既存のユニットテスト が動かない!
  103. 103. 全クラスにユニットテストがある S2TigerTestCaseクラスを継承 EasyMock + djUnit + JUnit3 ユニットテスト
  104. 104. ラムダ式を書いたら djUnitの部分で エラーが発生!
  105. 105. java.lang.Error: djUnit class load error (Class : com.example.ExampleClass) at jp.co.dgic.testing.common.DJUnitClassLoader.findClass (ClassLoader.java:424) at jp.co.dgic.testing.common.DJUnitClassLoader.loadClass (DJUnitClassLoader.java:45) at java.lang.ClassLoader.loadClass (ClassLoader.java:357) at com.example.ExampleClassTest.setUp (ExampleClassTest.java:79) djUnitでエラーが発生
  106. 106. djUnitはもう 更新がないため、 Java 8に対応していない
  107. 107. 内部で利用している ASM/BCELの メジャーバージョンアップ に対応していない
  108. 108. 逆に言うと、 djUnitを使っていない ユニットテストなら 動作する
  109. 109. 新規/変更したクラスのユニットテスト は、クラス単位ですべて書き換えた JMockit + JUnit4 ユニットテストの書き換え
  110. 110. モックライブラリ 機能が豊富 APIの呼び出し方が少し変わっている JMockitとは
  111. 111. JMockit or Mockito + PowerMockで検討 JMockitの方が、djUnitを使ったテストコー ドを単純に書き換えやすそうに感じた JMockitにした理由
  112. 112. JMockitへの書き換え S2TigerTestCase + EasyMock + djUnit public class ExampleActionTest extends S2TigerTestCase { private ExampleAction tester; @EasyMock(register = true) private ExampleService service; public void setUp() { ... } public void recordIndex() { ... EasyMock.expect(service.something()) .andReturn(serviceResult); } public void testIndex() throws Exception { Forward actual = tester.index(); assertEquals("/hoge/fuga.vm”, ...); }
  113. 113. JMockitへの書き換え JMockit public class ExampleControllerTest { @Tested private ExampleController tester; @Injectable private ExampleService service; @Test public void testIndex() throws Exception { ... new Expectations(tester) { { service.something(); result = serviceResult; } }; String actual = tester.index(); assertThat(actual, is("/hoge/fuga.vm")); }
  114. 114. JMockitでの書き方が 見慣れない感じですね
  115. 115. しかし チームへの導入で とくに抵抗はなく、 みな書き換えができた
  116. 116. ちなみに、 移行した機能の いわゆる結合テストは すべて手で再実施…
  117. 117. 後の方で 発覚したこと
  118. 118. 「あ、S2Velocityの 機能も移行しないと いけないのか」
  119. 119. S2Velocityを使い、Veloctityツールの インスタンスをS2Containerで 管理していた Velocityツールとは テンプレートファイルで利用する ユーティリティクラス Velocityの利用
  120. 120. 同一のテンプレートファイルを Springのときでも動作させるためには 同様の仕組みが必要となる ToolboxManager/VelocityToolboxView を実装 SpringのコンテナからVelocityContextに Velocityツールのインスタンスを渡す仕組み を実装した Velocityの利用
  121. 121. これで既存の テンプレートファイルを そのままSpring MVCから 利用できるようになった
  122. 122. Tipsを2点
  123. 123. Tips(1) 既存クラス @Component(name = ”example”, instance = InstanceType.REQUEST) public class ExampleClass { スコープの設定 @Componentアノテーションを読み取って スコープ設定をするSpringのResolverを 作成
  124. 124. Tips(2) 既存クラス @Component(name = ”example”, instance = InstanceType.REQUEST) public class ExampleClass { Bean名の設定 @Componentアノテーションを 読み取ってBean名を生成する SpringのBeanNameGeneratorを作成
  125. 125. その他
  126. 126. 2時間のミーティング 主に以前との差分、新しい書き方や 意味を説明 ほぼ抵抗なく習得へ進んだ メンバーへのレクチャー
  127. 127. Springで動作させる機能を増やす 2016年5月現在で3機能なので 機能単位での独立したサービスにする マイクロサービス化? 企画要件へ利用できるようになった 技術を適用する WebSocketを使うとユーザにどんな体験を 提供できるか 今後の展望
  128. 128. S2からの移行、 怖くないよ!
  129. 129. ご清聴 ありがとうございました!
  130. 130. Q&A

×