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

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

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

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

Yu Watanabe

January 24, 2013
Tweet

More Decks by Yu Watanabe

Other Decks in Technology

Transcript

  1. SpringMVCとmixer2で作る
    Webアプリのキホン
    Basic Web Application
    with SpringMVC & mixer2
    Spring勉強会 by #JSUG at VMWare-Japan
    2013-01-24

    View full-size slide

  2. プロローグ
    PROLOGUE
    2

    View full-size slide

  3. クリスマスイブのとあるツイート
    3

    View full-size slide

  4. MacBookじゃなくてスイマセン...
    4

    View full-size slide

  5. SpringMVCとmixer2で作る
    Webアプリのキホン
    なんかこのタイトルもダサく
    思えてきた...

    View full-size slide

  6. とりあえずタイトル変えてみる!

    View full-size slide

  7. Webデザイナーさんと
    仲良く仕事するための
    SpringMVCとmixer2
    2013-01-24
    Spring勉強会

    View full-size slide

  8. 自己紹介
    • わたなべ
    • SI屋の技術屋さん
    • @nabedge
    [email protected]
    • http://nabedge.blogspot.jp/
    8

    View full-size slide

  9. 目次
    1. SpringMVC
    2. テンプレートエンジン
    3. Mixer2をHelloWorldで解説
    4. Why mixer2 ?
    5. SpringMVCとmixer2の組み合わせの勘所
    6. コントローラとビューに対するテスト
    7. Webアプリの分割開発
    8. まとめ
    9. FAQ
    9

    View full-size slide

  10. 10
    1. Spring MVC

    View full-size slide

  11. SpringMVC
    • JavaでWebアプリをつくりためのMVCフレー
    ムワーク。
    • 生のサーブレット&JSPで作るより100倍作り
    やすい。
    • 大昔のStrutsより10倍は学習しやすい
    • ライバルとしてはSeasarのSAStrutsとか。
    • Spring3.Xになって以降はSAStrutsよりもさら
    に使いやすくなった!
    • 詳しくは「Spring3入門」を読みましょう。
    11

    View full-size slide

  12. 12
    2. テンプレートエンジン

    View full-size slide

  13. テンプレートエンジン
    13
    JSP:一番身近なテンプレートエンジン
    こんにちは
    <% if (name == null) { %>
    ゲストさん
    <% } else { %>
    <%= name %>さん
    <% } %>
    通常のJava言語、EL式、カスタムタグで書く

    View full-size slide

  14. テンプレートエンジン
    14
    Velocity:Javaでは老舗のテンプレートエンジン
    こんにちは
    #if (name == null) {
    ゲストさん
    #else
    ${name}さん
    #end
    VTL = Velocity Template Languageで書く

    View full-size slide

  15. テンプレートエンジン
    15
    FreeMarker:最近人気のテンプレートエンジン
    こんにちは
    <#if name?has_content>
    ${name}さん
    <#else>
    ゲストさん
    #if>
    FTL = Freemarker Template Languageで書く

    View full-size slide

  16. テンプレートエンジン
    16
    Mixer2:Webデザイナーと仲良く仕事するため
    のテンプレートエンジン
    こんにちは
    ななしさん
    String name = “ヤマダ”;
    Span span = html.getById(“name”, Span.class);
    span.getContent.clear();
    span.getContent.add(name);
    // これで ヤマダさん
    // になる
    テンプレートファイル(*.html)は純粋なXHTMLとCSS
    値の埋め込みやロジックは普通のJavaで書く(*.java)

    View full-size slide

  17. 17
    3. mixer2を
    HelloWorld(SpringMVC編)
    で解説
    http://mixer2.org/site/springmvcsample.html

    View full-size slide

  18. 補足:タグとJava型
    18
    … ⇔ org.mixer2.jaxb.xhtml.Html
    … ⇔ org.mixer2.jaxb.xhtml.Div
    (ほか全120種類くらいのタグすべてを実装済み)
    HTMLタグとJavaオブジェクトを相互マッピング
    タグの属性はJavaオブジェクトのプロパティにマッピング。
    setter/getterメソッドでアクセス

    をテンプレートとしてロードすると
    String id = div.getId(); // これでidに”foo”が入る
    (html4/5のすべての属性を実装済み)

    View full-size slide

  19. 補足:複数要素はListになる
    19


    Hello World
    foo
    bar


    index 型
    0 P
    1 String
    2 Span
    Html html = mixer2Engine
    .loadHtmlTemplate(
    “template.html”);
    java.util.List list
    = html.getBody()
    .getContent();
    listの中身
    template.html

    View full-size slide

  20. ちょっと一息
    20
    •水分補給
    •時間を確認
    10分か15分くらい?

    View full-size slide

  21. 21
    4. Why mixer2 ?

    View full-size slide

  22. 最大のメリット
    22
    htmlモックアップを
    JSPに書き変えずに
    そのまま使える

    View full-size slide

  23. 23
    デモ
    (フルーツショップサンプルアプリ編)
    https://github.com/nabedge/mixer2-
    sample/tree/master/mixer2-fruitshop-springmvc
    「github mixer2-fruitshop-springmvc」
    でググるとすぐ見つかります。

    View full-size slide

  24. 24
    5. Mixer2とSpringMVCを
    組み合わせる場合の勘所

    View full-size slide

  25. 勘所
    1. コントローラクラスの肥大化を防ぐ
    2. aタグやimgタグの相対パスの書き換え
    3. で静的リソースを出

    4. 上の2,3を生かすためのおススメディレ
    クトリ構造
    25

    View full-size slide

  26. 26
    コントローラクラスの肥大化を防ぐ

    View full-size slide

  27. ふつうのコントローラと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"%>


    商品名:${item.name}


    商品情報を表示するコントローラクラス
    JSP

    View full-size slide

  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());
    // 価格、説明、その他もろもろも...
    このへんが肥大化
    してしまう

    View full-size slide

  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行で済む

    View full-size slide

  30. コントローラの肥大化を防ぐ
    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から取得した商品情報

    View full-size slide

  31. 相対パスの書き換え
    31
    href="../m2template/index.html">


    左上のロゴはトップページへ
    のリンク
    テンプレートファイルではこうなってるけど
    href=“/[contextPath]/">


    実際の出力ではこうしなきゃならない

    View full-size slide

  32. 相対パスの書き換え
    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系のメソッドもあります。

    View full-size slide

  33. 相対パスの書き換え
    33
    //
    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/
    に置換している

    View full-size slide

  34. 34

    静的リソースを出力

    View full-size slide

  35. Java/Webアプリでの静的ファイルの配置
    35
    http://localhost:8080/[contextPath]/foo/bar.png
    src
    └─main
    ├─java
    ├─resources
    └─webapp
    └─foo
    └─bar.png
    ※maven標準ディレクトリ構造です
    普通ならdocroot配下に置く。
    さもないとブラウザからアク
    セス不可能

    View full-size slide

  36. Java/Webアプリでの静的ファイルの配置
    36
    src
    └─main
    ├─java
    ├─resources
    │ │ applicationContext.xml
    │ │
    │ └─m2mockup
    │ ├─m2static
    │ │ └─img
    │ │ logo.png
    │ │
    │ └─m2template
    │ index.html
    │ item.html

    └─webapp
    テンプレートhtmlと画像
    やCSSをまとめて
    resources配下に置く。
    クラスパス上に置くほう
    が、Javaコードから扱い
    やすいから!

    View full-size slide

  37. DispatcherServletのstatic resource機能
    • Spring3.X以降、DispatcherServletは、http
    リクエストをコントローラクラスに中継する機
    能だけでなく、静的リソースを直接レスポンス
    する機能がある
    37

    View full-size slide

  38. Java/Webアプリでの静的ファイルの配置
    38
    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レスポンスヘッダつきで返す

    View full-size slide

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

    View full-size slide

  40. だから、これがオススメ構造
    40
    src
    └─main
    ├─java
    ├─resources
    │ │ applicationContext.xml
    │ │
    │ └─m2mockup
    │ ├─m2static
    │ │ └─img
    │ │ logo.png
    │ │
    │ └─m2template
    │ index.html
    │ item.html

    └─webapp
    m2mockup配下にモック
    アップhtmlを作る
    画像やCSSはm2static配下に
    置いて、
    の設定での出力対象にする
    htmlテンプレートは
    m2template配下に

    View full-size slide

  41. これでデザイナとプログラマが仲良く仕事できる!
    41
    プログラマとデザイナの取り決め事項
    1. htmlモックアップは
    src/main/resources/m2mockup の下に作
    ろうぜ。
    2. ただし*.htmlはm2template,それ以外は
    m2staticの配下でたのむ。
    3. 商品情報のdivタグはid=“itemBox”にしよう。
    4. 商品名はspanタグでid=“itemName”
    5. …..その他の情報も同様にclass属性やid属性を決
    めておけばよい。

    View full-size slide

  42. 42
    もちろん
    「htmlをjspに書き変える」
    という退屈な作業は不要

    View full-size slide

  43. 43
    6. コントローラとビューに対するテスト

    View full-size slide

  44. ざっくりした流れ
    1. JunitコードのランナーとしてSpringJUnit4ClassRunner を
    使えば、DIコンテナが勝手にいい感じで起動してくれる。
    2. HttpServletRequest, HttpServletResponseのモックをイン
    スタンス化する
    3. モックのrequestにテスト対象のURIやパラメータをセット
    4. そのrequestオブジェクトをリクエストハンドラに渡すと疑似
    リクエストが発生し、コントローラクラスに渡される。
    5. コントローラの該当メソッドが戻り値として返す
    ModelAndViewオブジェクトにhtmlStringが入っている。
    6. このhtmlStringの中をMixer2Engineで再度Htmlオブジェク
    ト化する
    7. Htmlオブジェクトの中をAssertすればよい。
    44

    View full-size slide

  45. 45
    実際のテストコードで説明します
    https://github.com/nabedge/mixer2-
    sample/blob/master/mixer2-fruitshop-
    springmvc/src/test/java/org/mixer2/samp
    le/web/controller/ItemControllerTest.java

    View full-size slide

  46. 最後の給水
    46
    •水分補給
    •時間を確認
    40分くらい?

    View full-size slide

  47. 47
    Webアプリの分割開発

    View full-size slide

  48. 普通のWebアプリプロジェクト(maven形式)
    48
    Javaクラスとか
    このへん
    Javaクラス以外は
    src/main/webapp
    静的ファイル
    このへん
    JSPとか
    このへん

    View full-size slide

  49. 普通のWebアプリを分割開発するときのカベ
    1. Javaクラスや、その設定ファイル
    (*.properties,*.xml,*.sql)は別プロジェクト化
    してjar化してWebアプリ側から依存関係を
    つくればいい。
    – つまり、Javaライブラリは分割開発可能
    2. しかし、src/main/webapp 配下に置くような
    JSP,静的リソース、設定ファイル類(MVCで
    言うとViewの周辺)は、プロジェクト分割&
    別パッケージ化が難しい。
    49

    View full-size slide

  50. Mixer2を使うと
    1. Mixer2は、Viewを普通のjavaコードで取
    扱う。
    2. そのテンプレートもJavaコードから見ると
    ただのリソースファイルとして扱える。
    3. よって、普通のJavaライブラリ同様に分割
    が可能。
    50

    View full-size slide

  51. デモ
    • デモでお見せします。
    プロジェクト間依存関係はこんな感

    m2flowershop-web(.war)
    m2flowershop-front(.jar)
    m2flowershop-cart(.jar)
    m2flowershop-resource(.jar)
    51

    View full-size slide

  52. 後日談
    • ※結局、当日までにサンプルのコーディング
    が間に合わなかったのでこのデモはやってま
    せん。(^^;
    52

    View full-size slide

  53. 53
    7. まとめ

    View full-size slide

  54. まとめ
    • SpringMVCはシンプルで使いやすいフレームワーク
    • mixer2とSpringMVCは良いコンビ
    • ディレクトリ構造を考えたうえでSpringMVCの静的リ
    ソース出力機能と組み合わせれば、htmlモックアップ
    をjspに書き変える作業は不要
    • jspでは難しい、ビューに対するテストの自動化も可

    • htmlテンプレートファイルと静的ファイル(ex.画像)と
    Javaクラスをjarパッケージ化できるので、Webアプリ
    の分割開発すら可能。
    54

    View full-size slide

  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

    View full-size slide

  56. 56
    ご静聴ありがとうございました

    View full-size slide