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

        Basic Web Application
    with SpringMVC & mixer2
Spring勉強会 by #JSUG at VMWare-Japan
                        2013-01-24
プロローグ
PROLOGUE


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




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




                       4
なんかこのタイトルもダサく
思えてきた...


  SpringMVCとmixer2で作る
         Webアプリのキホン
とりあえずタイトル変えてみる!
Webデザイナーさんと
仲良く仕事するための
SpringMVCとmixer2
     2013-01-24
    Spring勉強会
自己紹介


       •   わたなべ
       •   SI屋の技術屋さん
       •   @nabedge
       •   nabedge@gmail.com
       •   http://nabedge.blogspot.jp/



                                         8
目次

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



                10
SpringMVC

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



                12
テンプレートエンジン
JSP:一番身近なテンプレートエンジン

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

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

   こんにちは
   <#if name?has_content>
     ${name}さん
   <#else>
     ゲストさん
   </#if>
   FTL = Freemarker Template Languageで書く
                                       15
テンプレートエンジン
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
3. mixer2を
 HelloWorld(SpringMVC編)
           で解説
http://mixer2.org/site/springmvcsample.html


                                              17
補足:タグと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
補足:複数要素は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
ちょっと一息




         •水分補給
         •時間を確認
         10分か15分くらい?
                   20
4. Why mixer2 ?



                  21
最大のメリット



 htmlモックアップを
 JSPに書き変えずに
 そのまま使える

               22
デモ
(フルーツショップサンプルアプリ編)
     https://github.com/nabedge/mixer2-
sample/tree/master/mixer2-fruitshop-springmvc


   「github mixer2-fruitshop-springmvc」
   でググるとすぐ見つかります。

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



                       24
勘所

1. コントローラクラスの肥大化を防ぐ
2. aタグやimgタグの相対パスの書き換え
3. <mvc:resources />で静的リソースを出
   力
4. 上の2,3を生かすためのおススメディレ
   クトリ構造



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




                   26
ふつうのコントローラと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
コントローラの肥大化を防ぐ
@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
コントローラの肥大化を防ぐ
@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
コントローラの肥大化を防ぐ
ヘルパーはごく単純な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
相対パスの書き換え
左上のロゴはトップページへ
のリンク
テンプレートファイルではこうなってるけど
<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
相対パスの書き換え
“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
相対パスの書き換え
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
<mvc:resources />で
 静的リソースを出力




                     34
Java/Webアプリでの静的ファイルの配置

http://localhost:8080/[contextPath]/foo/bar.png

  src
  └─main           普通ならdocroot配下に置く。
      ├─java       さもないとブラウザからアク
      ├─resources  セス不可能
      └─webapp
        └─foo
            └─bar.png

    ※maven標準ディレクトリ構造です
                                              35
Java/Webアプリでの静的ファイルの配置

src
└─main
    ├─java
    ├─resources
    │ │ applicationContext.xml
    │ │
    │ └─m2mockup                 テンプレートhtmlと画像
    │     ├─m2static
    │     │ └─img                やCSSをまとめて
    │     │          logo.png    resources配下に置く。
    │     │                      クラスパス上に置くほう
    │     └─m2template           が、Javaコードから扱い
    │            index.html      やすいから!
    │            item.html
    │
    └─webapp

                                                   36
DispatcherServletのstatic resource機能




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




                                         37
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
ただし、注意しないと、、、!
1. こういうディレクトリ構造で            2. こういう設定をしてしまうと
src                         <mvc:resources
└─main                        mapping=
    ├─java
    ├─resources
                                "/m2mockup/**"
    │ └─m2mockup              location=
    │     ├─img                 "classpath:/m2mockup/"/>
    │     │     logo.png
    │     └─item.html
    └─webapp


3. 画像やCSSだけでなく、テンプレートhtmlにもそのままアクセス
できてしまう!(もちろんまずい)
      http://…/[ContextPath]/m2mockup/img/logo.png
      http://…/[ContextPath]/m2mockup/item.html
                                                      39
だから、これがオススメ構造

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
これでデザイナとプログラマが仲良く仕事できる!




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



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




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

                                                44
実際のテストコードで説明します

 https://github.com/nabedge/mixer2-
 sample/blob/master/mixer2-fruitshop-
 springmvc/src/test/java/org/mixer2/samp
 le/web/controller/ItemControllerTest.java

                                             45
最後の給水




        •水分補給
        •時間を確認
        40分くらい?
                  46
Webアプリの分割開発




              47
普通のWebアプリプロジェクト(maven形式)
                     Javaクラス以外は
                     src/main/webapp



                           静的ファイル
                           このへん

 Javaクラスとか
 このへん


             JSPとか
             このへん

                                   48
普通のWebアプリを分割開発するときのカベ

1. Javaクラスや、その設定ファイル
   (*.properties,*.xml,*.sql)は別プロジェクト化
   してjar化してWebアプリ側から依存関係を
   つくればいい。
 – つまり、Javaライブラリは分割開発可能


2. しかし、src/main/webapp 配下に置くような
   JSP,静的リソース、設定ファイル類(MVCで
   言うとViewの周辺)は、プロジェクト分割&
   別パッケージ化が難しい。
                                     49
Mixer2を使うと

 1. Mixer2は、Viewを普通のjavaコードで取
    扱う。

 2. そのテンプレートもJavaコードから見ると
    ただのリソースファイルとして扱える。

 3. よって、普通のJavaライブラリ同様に分割
    が可能。


                                50
デモ

• デモでお見せします。
 プロジェクト間依存関係はこんな感
 じ
 m2flowershop-web(.war)
     m2flowershop-front(.jar)
     m2flowershop-cart(.jar)
       m2flowershop-resource(.jar)
                                     51
後日談

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




                         52
7. まとめ




         53
まとめ

• SpringMVCはシンプルで使いやすいフレームワーク
• mixer2とSpringMVCは良いコンビ
• ディレクトリ構造を考えたうえでSpringMVCの静的リ
  ソース出力機能と組み合わせれば、htmlモックアップ
  をjspに書き変える作業は不要
• jspでは難しい、ビューに対するテストの自動化も可
  能
• htmlテンプレートファイルと静的ファイル(ex.画像)と
  Javaクラスをjarパッケージ化できるので、Webアプリ
  の分割開発すら可能。
                              54
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

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