Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
SpringMVCとmixer2で作るWebアプリのキホン
Search
Yu Watanabe
January 24, 2013
Technology
0
95
SpringMVCとmixer2で作る Webアプリのキホン
WebデザイナーフレンドリーなJavaテンプレートエンジン"Mixer2"と、SpringMVCフレームワークを組み合わせての開発
Yu Watanabe
January 24, 2013
Tweet
Share
More Decks by Yu Watanabe
See All by Yu Watanabe
JUnitテストをCI環境で並列で実行する方法とその速度, スケーラビリティ
nabedge
5
2.2k
クラウド時代だからSpring-Retryフレームワーク
nabedge
0
98
ツール比較しながら語るO/RマッパーとDBマイグレーション
nabedge
0
77
JavaでWebサービスを作り続けるための戦略と戦術
nabedge
0
46
サーバーサイドな人がフロントエンド技術と仲良くするはじめの一歩
nabedge
0
41
Selenium再入門
nabedge
0
39
Webエンジニアがスタートダッシュをキメるためのローカル開発環境の勘所
nabedge
0
41
テストゼロからイチに進むための戦略と戦術
nabedge
0
47
jOOQってなんて読むの?から始めるSpringBootとO/Rマッパーの世界
nabedge
0
82
Other Decks in Technology
See All in Technology
データ基盤の成長を加速させる:アイスタイルにおける挑戦と教訓
tsuda7
3
620
Culture Deck
optfit
0
230
Datadog APM におけるトレース収集の流れ及び Retention Filters のはなし / datadog-apm-trace-retention-filters
k6s4i53rx
0
260
Active Directory の保護
eurekaberry
7
3.8k
AndroidデバイスにFTPサーバを建立する
e10dokup
0
210
20250208_OpenAIDeepResearchがやばいという話
doradora09
PRO
0
160
Googleマップ/Earthが一般化した 地図タイルのイマ
mapconcierge4agu
1
190
『AWS Distinguished Engineerに学ぶ リトライの技術』 #ARC403/Marc Brooker on Try again: The tools and techniques behind resilient systems
quiver
0
130
EDRからERM: PFN-SIRTが関わるセキュリティとリスクへの取り組み
pfn
PRO
0
160
optfit engineer culture deck
optfit
0
230
Ask! NIKKEIの運用基盤と改善に向けた取り組み / NIKKEI TECH TALK #30
kaitomajima
1
420
サーバーレスアーキテクチャと生成AIの融合 / Serverless Meets Generative AI
_kensh
12
2.8k
Featured
See All Featured
We Have a Design System, Now What?
morganepeng
51
7.4k
A Modern Web Designer's Workflow
chriscoyier
693
190k
Building Better People: How to give real-time feedback that sticks.
wjessup
366
19k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.1k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.6k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
[RailsConf 2023] Rails as a piece of cake
palkan
53
5.2k
The Language of Interfaces
destraynor
156
24k
Building Your Own Lightsaber
phodgson
104
6.2k
The Pragmatic Product Professional
lauravandoore
32
6.4k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
27
1.5k
A better future with KSS
kneath
238
17k
Transcript
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 •
[email protected]
•
http://nabedge.blogspot.jp/ 8
目次 1. SpringMVC 2. テンプレートエンジン 3. Mixer2をHelloWorldで解説 4. Why mixer2
? 5. SpringMVCとmixer2の組み合わせの勘所 6. コントローラとビューに対するテスト 7. Webアプリの分割開発 8. まとめ 9. FAQ 9
10 1. Spring MVC
SpringMVC • JavaでWebアプリをつくりためのMVCフレー ムワーク。 • 生のサーブレット&JSPで作るより100倍作り やすい。 • 大昔のStrutsより10倍は学習しやすい •
ライバルとしてはSeasarのSAStrutsとか。 • Spring3.Xになって以降はSAStrutsよりもさら に使いやすくなった! • 詳しくは「Spring3入門」を読みましょう。 11
12 2. テンプレートエンジン
テンプレートエンジン 13 JSP:一番身近なテンプレートエンジン こんにちは <% if (name == null) {
%> ゲストさん <% } else { %> <%= name %>さん <% } %> 通常のJava言語、EL式、カスタムタグで書く
テンプレートエンジン 14 Velocity:Javaでは老舗のテンプレートエンジン こんにちは #if (name == null) { ゲストさん
#else ${name}さん #end VTL = Velocity Template Languageで書く
テンプレートエンジン 15 FreeMarker:最近人気のテンプレートエンジン こんにちは <#if name?has_content> ${name}さん <#else> ゲストさん </#if>
FTL = Freemarker Template Languageで書く
テンプレートエンジン 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)
17 3. mixer2を HelloWorld(SpringMVC編) で解説 http://mixer2.org/site/springmvcsample.html
補足:タグと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のすべての属性を実装済み)
補足:複数要素は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
ちょっと一息 20 •水分補給 •時間を確認 10分か15分くらい?
21 4. Why mixer2 ?
最大のメリット 22 htmlモックアップを JSPに書き変えずに そのまま使える
23 デモ (フルーツショップサンプルアプリ編) https://github.com/nabedge/mixer2- sample/tree/master/mixer2-fruitshop-springmvc 「github mixer2-fruitshop-springmvc」 でググるとすぐ見つかります。
24 5. Mixer2とSpringMVCを 組み合わせる場合の勘所
勘所 1. コントローラクラスの肥大化を防ぐ 2. aタグやimgタグの相対パスの書き換え 3. <mvc:resources />で静的リソースを出 力 4.
上の2,3を生かすためのおススメディレ クトリ構造 25
26 コントローラクラスの肥大化を防ぐ
ふつうのコントローラと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
コントローラの肥大化を防ぐ 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()); // 価格、説明、その他もろもろも... このへんが肥大化 してしまう
コントローラの肥大化を防ぐ 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行で済む
コントローラの肥大化を防ぐ 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から取得した商品情報
相対パスの書き換え 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> 実際の出力ではこうしなきゃならない
相対パスの書き換え 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系のメソッドもあります。
相対パスの書き換え 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/ に置換している
34 <mvc:resources />で 静的リソースを出力
Java/Webアプリでの静的ファイルの配置 35 http://localhost:8080/[contextPath]/foo/bar.png src └─main ├─java ├─resources └─webapp └─foo └─bar.png
※maven標準ディレクトリ構造です 普通ならdocroot配下に置く。 さもないとブラウザからアク セス不可能
Java/Webアプリでの静的ファイルの配置 36 src └─main ├─java ├─resources │ │ applicationContext.xml │
│ │ └─m2mockup │ ├─m2static │ │ └─img │ │ logo.png │ │ │ └─m2template │ index.html │ item.html │ └─webapp テンプレートhtmlと画像 やCSSをまとめて resources配下に置く。 クラスパス上に置くほう が、Javaコードから扱い やすいから!
DispatcherServletのstatic resource機能 • Spring3.X以降、DispatcherServletは、http リクエストをコントローラクラスに中継する機 能だけでなく、静的リソースを直接レスポンス する機能がある 37
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レスポンスヘッダつきで返す
ただし、注意しないと、、、! 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
だから、これがオススメ構造 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配下に
これでデザイナとプログラマが仲良く仕事できる! 41 プログラマとデザイナの取り決め事項 1. htmlモックアップは src/main/resources/m2mockup の下に作 ろうぜ。 2. ただし*.htmlはm2template,それ以外は
m2staticの配下でたのむ。 3. 商品情報のdivタグはid=“itemBox”にしよう。 4. 商品名はspanタグでid=“itemName” 5. …..その他の情報も同様にclass属性やid属性を決 めておけばよい。
42 もちろん 「htmlをjspに書き変える」 という退屈な作業は不要
43 6. コントローラとビューに対するテスト
ざっくりした流れ 1. JunitコードのランナーとしてSpringJUnit4ClassRunner を 使えば、DIコンテナが勝手にいい感じで起動してくれる。 2. HttpServletRequest, HttpServletResponseのモックをイン スタンス化する 3.
モックのrequestにテスト対象のURIやパラメータをセット 4. そのrequestオブジェクトをリクエストハンドラに渡すと疑似 リクエストが発生し、コントローラクラスに渡される。 5. コントローラの該当メソッドが戻り値として返す ModelAndViewオブジェクトにhtmlStringが入っている。 6. このhtmlStringの中をMixer2Engineで再度Htmlオブジェク ト化する 7. Htmlオブジェクトの中をAssertすればよい。 44
45 実際のテストコードで説明します https://github.com/nabedge/mixer2- sample/blob/master/mixer2-fruitshop- springmvc/src/test/java/org/mixer2/samp le/web/controller/ItemControllerTest.java
最後の給水 46 •水分補給 •時間を確認 40分くらい?
47 Webアプリの分割開発
普通のWebアプリプロジェクト(maven形式) 48 Javaクラスとか このへん Javaクラス以外は src/main/webapp 静的ファイル このへん JSPとか このへん
普通の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
53 7. まとめ
まとめ • 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 ご静聴ありがとうございました