Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

SpringMVCとmixer2で作るWebアプリのキホン 2013-01-24 Spring勉強会 #jsug

6,157 views

Published on

Published in: Technology
  • この資料は少し古くなっています。
    Feb 2014現在のmixer2-1.2.22 にはSpringMVC用の
    ViewResolverが同梱されていますのでそれを使うべきです
    詳しくは公式サイトをどうぞ。
    (en) http://mixer2.org/site/springmvc.html
    (jp) http://mixer2.org/site/ja/springmvc.html
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

SpringMVCとmixer2で作るWebアプリのキホン 2013-01-24 Spring勉強会 #jsug

  1. 1. SpringMVCとmixer2で作る Webアプリのキホン Basic Web Application with SpringMVC & mixer2Spring勉強会 by #JSUG at VMWare-Japan 2013-01-24
  2. 2. プロローグPROLOGUE 2
  3. 3. クリスマスイブのとあるツイート 3
  4. 4. MacBookじゃなくてスイマセン... 4
  5. 5. なんかこのタイトルもダサく思えてきた... SpringMVCとmixer2で作る Webアプリのキホン
  6. 6. とりあえずタイトル変えてみる!
  7. 7. Webデザイナーさんと仲良く仕事するためのSpringMVCとmixer2 2013-01-24 Spring勉強会
  8. 8. 自己紹介 • わたなべ • SI屋の技術屋さん • @nabedge • nabedge@gmail.com • http://nabedge.blogspot.jp/ 8
  9. 9. 目次1. SpringMVC2. テンプレートエンジン3. Mixer2をHelloWorldで解説4. Why mixer2 ?5. SpringMVCとmixer2の組み合わせの勘所6. コントローラとビューに対するテスト7. Webアプリの分割開発8. まとめ9. FAQ 9
  10. 10. 1. Spring MVC 10
  11. 11. SpringMVC• JavaでWebアプリをつくりためのMVCフレー ムワーク。• 生のサーブレット&JSPで作るより100倍作り やすい。• 大昔のStrutsより10倍は学習しやすい• ライバルとしてはSeasarのSAStrutsとか。• Spring3.Xになって以降はSAStrutsよりもさら に使いやすくなった!• 詳しくは「Spring3入門」を読みましょう。 11
  12. 12. 2. テンプレートエンジン 12
  13. 13. テンプレートエンジンJSP:一番身近なテンプレートエンジン こんにちは <% if (name == null) { %> ゲストさん <% } else { %> <%= name %>さん <% } %> 通常のJava言語、EL式、カスタムタグで書く 13
  14. 14. テンプレートエンジンVelocity:Javaでは老舗のテンプレートエンジン こんにちは #if (name == null) { ゲストさん #else ${name}さん #end VTL = Velocity Template Languageで書く 14
  15. 15. テンプレートエンジンFreeMarker:最近人気のテンプレートエンジン こんにちは <#if name?has_content> ${name}さん <#else> ゲストさん </#if> FTL = Freemarker Template Languageで書く 15
  16. 16. テンプレートエンジンMixer2:Webデザイナーと仲良く仕事するためのテンプレートエンジンテンプレートファイル(*.html)は純粋なXHTMLとCSS こんにちは <span id=“name”>ななし</span>さん値の埋め込みやロジックは普通のJavaで書く(*.java) String name = “ヤマダ”; Span span = html.getById(“name”, Span.class); span.getContent.clear(); span.getContent.add(name); // これで <span id=“name”>ヤマダ</span>さん // になる 16
  17. 17. 3. mixer2を HelloWorld(SpringMVC編) で解説http://mixer2.org/site/springmvcsample.html 17
  18. 18. 補足:タグとJava型HTMLタグとJavaオブジェクトを相互マッピング <html>…</html> ⇔ org.mixer2.jaxb.xhtml.Html <div>…</div> ⇔ org.mixer2.jaxb.xhtml.Div (ほか全120種類くらいのタグすべてを実装済み)タグの属性はJavaオブジェクトのプロパティにマッピング。setter/getterメソッドでアクセス <div id=“foo”>…</div> をテンプレートとしてロードすると String id = div.getId(); // これでidに”foo”が入る (html4/5のすべての属性を実装済み) 18
  19. 19. 補足:複数要素はListになるtemplate.html listの中身<html> index 型<body> 0 P <p>Hello World</p> foo 1 String <span>bar</span></body> 2 Span</html>Html html = mixer2Engine java.util.List<Object> list .loadHtmlTemplate( = html.getBody() “template.html”); .getContent(); 19
  20. 20. ちょっと一息 •水分補給 •時間を確認 10分か15分くらい? 20
  21. 21. 4. Why mixer2 ? 21
  22. 22. 最大のメリット htmlモックアップを JSPに書き変えずに そのまま使える 22
  23. 23. デモ(フルーツショップサンプルアプリ編) https://github.com/nabedge/mixer2-sample/tree/master/mixer2-fruitshop-springmvc 「github mixer2-fruitshop-springmvc」 でググるとすぐ見つかります。 23
  24. 24. 5. Mixer2とSpringMVCを組み合わせる場合の勘所 24
  25. 25. 勘所1. コントローラクラスの肥大化を防ぐ2. aタグやimgタグの相対パスの書き換え3. <mvc:resources />で静的リソースを出 力4. 上の2,3を生かすためのおススメディレ クトリ構造 25
  26. 26. コントローラクラスの肥大化を防ぐ 26
  27. 27. ふつうのコントローラとJSP商品情報を表示するコントローラクラス @Controller public class ItemController { @RequestMapping(value = "/item/{itemId}") public ModelAndView showItem(@PathVariable long itemId) { // DBから商品情報を取得 Item item = itemService.getItem(itemId); // modelAndViewにitemを詰めて返す retern new ModelAndView(“item.jsp”, “item”, item); }JSP <%@page pageEncoding="UTF-8"%> <html> <body> <span>商品名:${item.name}</span> </body> </html> 27
  28. 28. コントローラの肥大化を防ぐ@RequestMapping(value = "/item/{itemId}")public ModelAndView showItem(@PathVariable long itemId) { // DBから商品情報を取得 Item item = itemService.getItem(itemId); // テンプレートのロード String mainTemplate = "classpath:m2mockup/m2template/item.html" File file = ResourceUtils.getFile(mainTemplate); Html html = mixer2Engine.loadHtmlTemplate(file); このへんが肥大化 してしまう // 商品情報のdivタグ Div itemBox = html.getBody().getById("itemBox", Div.class); // 商品名を書き込む itemBox.getById("itemName", H1.class).getContent().clear(); itemBox.getById("itemName", H1.class).getContent().add(item.getName()); // 価格、説明、その他もろもろも... 28
  29. 29. コントローラの肥大化を防ぐ@RequestMapping(value = "/item/{itemId}")public ModelAndView showItem(@PathVariable long itemId) { // DBから商品情報を取得 Item item = itemService.getItem(itemId); // テンプレートのロード String mainTemplate = "classpath:m2mockup/m2template/item.html" File file = ResourceUtils.getFile(mainTemplate); Html html = mixer2Engine.loadHtmlTemplate(file); // 商品情報を埋め込む ItemHelper.replaceItemBox(html, item); …… ヘルパークラスに切り 出せば1行で済む 29
  30. 30. コントローラの肥大化を防ぐヘルパーはごく単純なstaticメソッドでよいpublic class ItemHelper { テンプレのhtml DBから取得した商品情報 public static void replaceItemBox(Html html, Item item) { // 商品情報を入れるdivタグを取得 Div itemBox = html.getBody().getById("itemBox", Div.class); // divの中のH1やSpanの中にDBから取得した値を入れる itemBox.getById("itemName", H1.class).getContent().clear(); itemBox.getById("itemName", H1.class).getContent().add(item.getName()); itemBox.getById("itemPrice", Span.class).getContent().clear(); itemBox.getById("itemPrice", Span.class).getContent().add( item.getPrice().toString()); itemBox.getById("itemDescription", Div.class).getContent().clear(); itemBox.getById("itemDescription", Div.class).getContent().add( item.getDescription()); 30
  31. 31. 相対パスの書き換え左上のロゴはトップページへのリンクテンプレートファイルではこうなってるけど<a class=“topPageAnchor” href="../m2template/index.html"> <img src="../m2static/img/fruitshop-logo.png" /></a>実際の出力ではこうしなきゃならない<a class=“topPageAnchor” href=“/[contextPath]/"> <img src="/[contextPath]/m2static/img/fruitshop-logo.png" /></a> 31
  32. 32. 相対パスの書き換え“topPageAnchor”というclass属性を持つすべてのaタグのhref属性を書き変えるString ctx = "xxx"; // コンテキストパスを取得しておくfor (A a : html.getDescendants("topPageAnchor", A.class)) { a.setHref(ctx + "/");}Mixer2ではすべてのタグ型が下記のメソッドを持っている• getDescendants()メソッド:該当するすべての子孫タグをList で取得• getById()メソッド:id属性でタグを1個だけ取得• 他にもreplace系とかremove系のメソッドもあります。 32
  33. 33. 相対パスの書き換えImgタグのsrc属性、styleタグのhref属性なども同様// <img src="" />for (Img img : tagObj.getDescendants(Img.class)) { if (img.isSetSrc()) { String src = img.getSrc(); img.setSrc(convertPath(src)); }} convertPathメソッドは、 ../m2static/ のような文字列を /[contextPath]/m2static/ に置換している 33
  34. 34. <mvc:resources />で 静的リソースを出力 34
  35. 35. Java/Webアプリでの静的ファイルの配置http://localhost:8080/[contextPath]/foo/bar.png src └─main 普通ならdocroot配下に置く。 ├─java さもないとブラウザからアク ├─resources セス不可能 └─webapp └─foo └─bar.png ※maven標準ディレクトリ構造です 35
  36. 36. Java/Webアプリでの静的ファイルの配置src└─main ├─java ├─resources │ │ applicationContext.xml │ │ │ └─m2mockup テンプレートhtmlと画像 │ ├─m2static │ │ └─img やCSSをまとめて │ │ logo.png resources配下に置く。 │ │ クラスパス上に置くほう │ └─m2template が、Javaコードから扱い │ index.html やすいから! │ item.html │ └─webapp 36
  37. 37. DispatcherServletのstatic resource機能 • Spring3.X以降、DispatcherServletは、http リクエストをコントローラクラスに中継する機 能だけでなく、静的リソースを直接レスポンス する機能がある 37
  38. 38. Java/Webアプリでの静的ファイルの配置src/main/resources/mvc-dispatcher-servlet.xml の抜粋 <mvc:resources mapping="/m2static/**" location="classpath:/m2mockup/m2static/" cache-period="60" /> 1. http://.../contextPath/m2static/** というURLへの アクセスに対して 2. クラスパスから /m2mockup/m2static/** というリ ソースを探してそれを返す 3. そのとき Cache-Control: max-age=60 のような httpレスポンスヘッダつきで返す 38
  39. 39. ただし、注意しないと、、、!1. こういうディレクトリ構造で 2. こういう設定をしてしまうとsrc <mvc:resources└─main mapping= ├─java ├─resources "/m2mockup/**" │ └─m2mockup location= │ ├─img "classpath:/m2mockup/"/> │ │ logo.png │ └─item.html └─webapp3. 画像やCSSだけでなく、テンプレートhtmlにもそのままアクセスできてしまう!(もちろんまずい) http://…/[ContextPath]/m2mockup/img/logo.png http://…/[ContextPath]/m2mockup/item.html 39
  40. 40. だから、これがオススメ構造src└─main ├─java m2mockup配下にモック ├─resources アップhtmlを作る │ │ applicationContext.xml │ │ │ └─m2mockup 画像やCSSはm2static配下に │ ├─m2static 置いて、 <mvc:resources /> │ │ └─img │ │ logo.png の設定での出力対象にする │ │ │ └─m2template │ index.html │ item.html │ └─webapp htmlテンプレートは m2template配下に 40
  41. 41. これでデザイナとプログラマが仲良く仕事できる! プログラマとデザイナの取り決め事項 1. htmlモックアップは src/main/resources/m2mockup の下に作 ろうぜ。 2. ただし*.htmlはm2template,それ以外は m2staticの配下でたのむ。 3. 商品情報のdivタグはid=“itemBox”にしよう。 4. 商品名はspanタグでid=“itemName” 5. …..その他の情報も同様にclass属性やid属性を決 めておけばよい。 41
  42. 42. もちろん「htmlをjspに書き変える」という退屈な作業は不要 42
  43. 43. 6. コントローラとビューに対するテスト 43
  44. 44. ざっくりした流れ1. JunitコードのランナーとしてSpringJUnit4ClassRunner を 使えば、DIコンテナが勝手にいい感じで起動してくれる。2. HttpServletRequest, HttpServletResponseのモックをイン スタンス化する3. モックのrequestにテスト対象のURIやパラメータをセット4. そのrequestオブジェクトをリクエストハンドラに渡すと疑似 リクエストが発生し、コントローラクラスに渡される。5. コントローラの該当メソッドが戻り値として返す ModelAndViewオブジェクトにhtmlStringが入っている。6. このhtmlStringの中をMixer2Engineで再度Htmlオブジェク ト化する7. Htmlオブジェクトの中をAssertすればよい。 44
  45. 45. 実際のテストコードで説明します https://github.com/nabedge/mixer2- sample/blob/master/mixer2-fruitshop- springmvc/src/test/java/org/mixer2/samp le/web/controller/ItemControllerTest.java 45
  46. 46. 最後の給水 •水分補給 •時間を確認 40分くらい? 46
  47. 47. Webアプリの分割開発 47
  48. 48. 普通のWebアプリプロジェクト(maven形式) Javaクラス以外は src/main/webapp 静的ファイル このへん Javaクラスとか このへん JSPとか このへん 48
  49. 49. 普通のWebアプリを分割開発するときのカベ1. Javaクラスや、その設定ファイル (*.properties,*.xml,*.sql)は別プロジェクト化 してjar化してWebアプリ側から依存関係を つくればいい。 – つまり、Javaライブラリは分割開発可能2. しかし、src/main/webapp 配下に置くような JSP,静的リソース、設定ファイル類(MVCで 言うとViewの周辺)は、プロジェクト分割& 別パッケージ化が難しい。 49
  50. 50. Mixer2を使うと 1. Mixer2は、Viewを普通のjavaコードで取 扱う。 2. そのテンプレートもJavaコードから見ると ただのリソースファイルとして扱える。 3. よって、普通のJavaライブラリ同様に分割 が可能。 50
  51. 51. デモ• デモでお見せします。 プロジェクト間依存関係はこんな感 じ m2flowershop-web(.war) m2flowershop-front(.jar) m2flowershop-cart(.jar) m2flowershop-resource(.jar) 51
  52. 52. 後日談• ※結局、当日までにサンプルのコーディング が間に合わなかったのでこのデモはやってま せん。(^^; 52
  53. 53. 7. まとめ 53
  54. 54. まとめ• SpringMVCはシンプルで使いやすいフレームワーク• mixer2とSpringMVCは良いコンビ• ディレクトリ構造を考えたうえでSpringMVCの静的リ ソース出力機能と組み合わせれば、htmlモックアップ をjspに書き変える作業は不要• jspでは難しい、ビューに対するテストの自動化も可 能• htmlテンプレートファイルと静的ファイル(ex.画像)と Javaクラスをjarパッケージ化できるので、Webアプリ の分割開発すら可能。 54
  55. 55. FAQ• Q. モックアップHTMLはクラスパス上に置かなきゃダメですか? – A. Mixer2EngineのloadHtmlTemplateメソッドはjava.io.File, String, StringBufferのいずれかでテンプレートhtmlを読めます。したがってOSのファイ ルシステム上でもDB上でもどこでもOKです。 – ※後日談:ver1.1.14 以降、InputStreamからも読めるようになりました。• Q. WEB-INF/view/mixer2view.jsp を通じて結局jspを使っているのはダ サくないですか? – A. 実はその通りです。本来は、コントローラのメソッドの戻り値としてHtmlオブ ジェクトを返すだけで済むようなViewResolverを実装するべきです。誰か作っ て!(Pull Request熱烈歓迎!)• Q. mixer2を使う場合はJSPは完全に排除しなきゃだめですか?既存の taglibも使いたいのですが? – A. 可能です。たとえば、 *.htmlで用意したテンプレートを読み込んで、その一部 のタグだけを部分マーシャルし、jsp上に埋め込むことも可能です。 55
  56. 56. ご静聴ありがとうございました 56

×