Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SpringMVCとmixer2で作る Webアプリのキホン

Avatar for Yu Watanabe Yu Watanabe
January 24, 2013

SpringMVCとmixer2で作る Webアプリのキホン

WebデザイナーフレンドリーなJavaテンプレートエンジン"Mixer2"と、SpringMVCフレームワークを組み合わせての開発

Avatar for Yu Watanabe

Yu Watanabe

January 24, 2013
Tweet

More Decks by Yu Watanabe

Other Decks in Technology

Transcript

  1. 目次 1. SpringMVC 2. テンプレートエンジン 3. Mixer2をHelloWorldで解説 4. Why mixer2

    ? 5. SpringMVCとmixer2の組み合わせの勘所 6. コントローラとビューに対するテスト 7. Webアプリの分割開発 8. まとめ 9. FAQ 9
  2. SpringMVC • JavaでWebアプリをつくりためのMVCフレー ムワーク。 • 生のサーブレット&JSPで作るより100倍作り やすい。 • 大昔のStrutsより10倍は学習しやすい •

    ライバルとしてはSeasarのSAStrutsとか。 • Spring3.Xになって以降はSAStrutsよりもさら に使いやすくなった! • 詳しくは「Spring3入門」を読みましょう。 11
  3. テンプレートエンジン 13 JSP:一番身近なテンプレートエンジン こんにちは <% if (name == null) {

    %> ゲストさん <% } else { %> <%= name %>さん <% } %> 通常のJava言語、EL式、カスタムタグで書く
  4. テンプレートエンジン 16 Mixer2:Webデザイナーと仲良く仕事するため のテンプレートエンジン こんにちは <span id=“name”>ななし</span>さん String name =

    “ヤマダ”; Span span = html.getById(“name”, Span.class); span.getContent.clear(); span.getContent.add(name); // これで <span id=“name”>ヤマダ</span>さん // になる テンプレートファイル(*.html)は純粋なXHTMLとCSS 値の埋め込みやロジックは普通のJavaで書く(*.java)
  5. 補足:タグとJava型 18 <html>…</html> ⇔ org.mixer2.jaxb.xhtml.Html <div>…</div> ⇔ org.mixer2.jaxb.xhtml.Div (ほか全120種類くらいのタグすべてを実装済み) HTMLタグとJavaオブジェクトを相互マッピング

    タグの属性はJavaオブジェクトのプロパティにマッピング。 setter/getterメソッドでアクセス <div id=“foo”>…</div> をテンプレートとしてロードすると String id = div.getId(); // これでidに”foo”が入る (html4/5のすべての属性を実装済み)
  6. 補足:複数要素はListになる 19 <html> <body> <p>Hello World</p> foo <span>bar</span> </body> </html>

    index 型 0 P 1 String 2 Span Html html = mixer2Engine .loadHtmlTemplate( “template.html”); java.util.List<Object> list = html.getBody() .getContent(); listの中身 template.html
  7. ふつうのコントローラとJSP 27 @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); } <%@page pageEncoding="UTF-8"%> <html> <body> <span>商品名:${item.name}</span> </body> </html> 商品情報を表示するコントローラクラス JSP
  8. コントローラの肥大化を防ぐ 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()); // 価格、説明、その他もろもろも... このへんが肥大化 してしまう
  9. コントローラの肥大化を防ぐ 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行で済む
  10. コントローラの肥大化を防ぐ 30 public class ItemHelper { 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()); ヘルパーはごく単純なstaticメソッドでよい テンプレのhtml DBから取得した商品情報
  11. 相対パスの書き換え 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> 実際の出力ではこうしなきゃならない
  12. 相対パスの書き換え 32 String ctx = "xxx"; // コンテキストパスを取得しておく for (A

    a : html.getDescendants("topPageAnchor", A.class)) { a.setHref(ctx + "/"); } “topPageAnchor”というclass属性を持つすべてのaタグのhref属 性を書き変える Mixer2ではすべてのタグ型が下記のメソッドを持っている • getDescendants()メソッド:該当するすべての子孫タグをList で取得 • getById()メソッド:id属性でタグを1個だけ取得 • 他にもreplace系とかremove系のメソッドもあります。
  13. 相対パスの書き換え 33 // <img src="" /> for (Img img :

    tagObj.getDescendants(Img.class)) { if (img.isSetSrc()) { String src = img.getSrc(); img.setSrc(convertPath(src)); } } Imgタグのsrc属性、styleタグのhref属性なども同様 convertPathメソッドは、 ../m2static/ のような文字列を /[contextPath]/m2static/ に置換している
  14. Java/Webアプリでの静的ファイルの配置 35 http://localhost:8080/[contextPath]/foo/bar.png src └─main ├─java ├─resources └─webapp └─foo └─bar.png

    ※maven標準ディレクトリ構造です 普通ならdocroot配下に置く。 さもないとブラウザからアク セス不可能
  15. Java/Webアプリでの静的ファイルの配置 36 src └─main ├─java ├─resources │ │ applicationContext.xml │

    │ │ └─m2mockup │ ├─m2static │ │ └─img │ │ logo.png │ │ │ └─m2template │ index.html │ item.html │ └─webapp テンプレートhtmlと画像 やCSSをまとめて resources配下に置く。 クラスパス上に置くほう が、Javaコードから扱い やすいから!
  16. Java/Webアプリでの静的ファイルの配置 38 <mvc:resources mapping="/m2static/**" location="classpath:/m2mockup/m2static/" cache-period="60" /> src/main/resources/mvc-dispatcher-servlet.xml の抜粋 1.

    http://.../contextPath/m2static/** というURLへの アクセスに対して 2. クラスパスから /m2mockup/m2static/** というリ ソースを探してそれを返す 3. そのとき Cache-Control: max-age=60 のような httpレスポンスヘッダつきで返す
  17. ただし、注意しないと、、、! 39 src └─main ├─java ├─resources │ └─m2mockup │ ├─img

    │ │ logo.png │ └─item.html └─webapp 1. こういうディレクトリ構造で <mvc:resources mapping= "/m2mockup/**" location= "classpath:/m2mockup/"/> 2. こういう設定をしてしまうと 3. 画像やCSSだけでなく、テンプレートhtmlにもそのままアクセス できてしまう!(もちろんまずい) http://…/[ContextPath]/m2mockup/img/logo.png http://…/[ContextPath]/m2mockup/item.html
  18. だから、これがオススメ構造 40 src └─main ├─java ├─resources │ │ applicationContext.xml │

    │ │ └─m2mockup │ ├─m2static │ │ └─img │ │ logo.png │ │ │ └─m2template │ index.html │ item.html │ └─webapp m2mockup配下にモック アップhtmlを作る 画像やCSSはm2static配下に 置いて、 <mvc:resources /> の設定での出力対象にする htmlテンプレートは m2template配下に
  19. これでデザイナとプログラマが仲良く仕事できる! 41 プログラマとデザイナの取り決め事項 1. htmlモックアップは src/main/resources/m2mockup の下に作 ろうぜ。 2. ただし*.htmlはm2template,それ以外は

    m2staticの配下でたのむ。 3. 商品情報のdivタグはid=“itemBox”にしよう。 4. 商品名はspanタグでid=“itemName” 5. …..その他の情報も同様にclass属性やid属性を決 めておけばよい。
  20. ざっくりした流れ 1. JunitコードのランナーとしてSpringJUnit4ClassRunner を 使えば、DIコンテナが勝手にいい感じで起動してくれる。 2. HttpServletRequest, HttpServletResponseのモックをイン スタンス化する 3.

    モックのrequestにテスト対象のURIやパラメータをセット 4. そのrequestオブジェクトをリクエストハンドラに渡すと疑似 リクエストが発生し、コントローラクラスに渡される。 5. コントローラの該当メソッドが戻り値として返す ModelAndViewオブジェクトにhtmlStringが入っている。 6. このhtmlStringの中をMixer2Engineで再度Htmlオブジェク ト化する 7. Htmlオブジェクトの中をAssertすればよい。 44
  21. まとめ • SpringMVCはシンプルで使いやすいフレームワーク • mixer2とSpringMVCは良いコンビ • ディレクトリ構造を考えたうえでSpringMVCの静的リ ソース出力機能と組み合わせれば、htmlモックアップ をjspに書き変える作業は不要 •

    jspでは難しい、ビューに対するテストの自動化も可 能 • htmlテンプレートファイルと静的ファイル(ex.画像)と Javaクラスをjarパッケージ化できるので、Webアプリ の分割開発すら可能。 54
  22. 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