なみひらブログ

学んだことを日々記録する。~ since 2012/06/24 ~

tilesを使った時のJSPの評価タイミングについて

背景

tilesを使ってJSPファイルを分割+統合(?)したときに、思っていた動きと違った部分があったのでメモっときます。

結論

tilesで分割したJSPはそれぞれで評価される(=tilesで統合したあとの構造で評価はされない)。

前提

以下のようなファイルの配置の例で記載します。(一部略)

/WEB-INF
  |- /tiles/definition.xml
  |- /views/common
             |-layout.jsp
             |-navigation.jsp
  • tiles定義ファイル
    • /WEB-INF/tiles/definition.xml
    • ヘッダ部分(header.jsp)とナビゲーションバー部分(navigation.jsp)をそれぞれ定義。
<tiles-definitions>
  <definition name=".baseLayout" template="/WEB-INF/views/common/layout.jsp">
    <put-attribute name="header" value="/WEB-INF/views/common/header.jsp" />
    <put-attribute name="navigation" value="/WEB-INF/views/common/navigation.jsp" />
  </definition>
  • tilesレイアウト定義
    • /WEB-INF/views/common/layout.xml
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head>
  <tiles:insertAttribute name="header" />
</head>

<body>
  <tiles:insertAttribute name="navigation" />
</body>
</html>
  • ヘッダ定義
    • /WEB-INF/views/common/header.jsp
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<link rel="stylesheet" type="text/css" href="./resources/css/custom.css">
(略)
<ul class="nav navbar-nav">
  <li><a href="app/view/users">一覧を見る<span class="sr-only">(current)</span></a></li>
</ul>
(略)

最初にやろうと思ったこと

  • ヘッダ定義で"./resources/css/custom.css"のように相対パスで定義していたが、パスがあっちこっち行った場合相対パスがうまく一致せずうまく読めなくなった。*1
    • アクセス先が/app/homeとか/app/users/1とかだと./を指す場所はそれぞれ異なる。
    • コンテキストパス指定の絶対パスで指定したほうが楽。(/app/resource/css/custom.cssとか)
  • ナビゲーションバー定義でコンテキストパスがいろんなところに出てきてメンテが大変そうなので、文字列指定をやめたい。

次にやろうと思ったこと

  • コンテキストパスは以下のように定義できるので使う。
    • ヘッダ定義で使った例
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
<link rel="stylesheet" type="text/css" href="${contextPath}/resources/css/custom.css">
  • この定義はすべての画面でtiles的に取り込まれるので、ナビゲーションバー定義のほうでもそのまま使ってみる。<- この考えが間違い(;´Д`)
(略)
<ul class="nav navbar-nav">
  <li><a href="${contextPath}/view/users">一覧を見る<span class="sr-only">(current)</span></a></li>
</ul>
(略)

起きた問題

ナビゲーションバー内のリンクを押しても"http://example.com/view/users"に飛ばされる(=コンテキストパスがない)

対応

ナビゲーションバー定義にヘッダ定義にしたようにコンテキストパス定義を書くと、ちゃんと意図した動きになった("http://example.com/app/view/users"への遷移)ので、各JSPごとに評価されていることに気づく。
で、結局以下のように定義しました。

/WEB-INF
  |- /tiles/definition.xml
  |- /views/common
             |-layout.jsp
             |-navigation.jsp
             |-variable.jsp    <- 各種変数定義用JSP
  • 変数定義用JSP
    • JSP内で使いたい変数はこちらのファイルで定義する
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
  • ヘッダ定義(修正版)
    • 定義を読み込む。
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>

<%@ include file="variable.jsp" %>

<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<link rel="stylesheet" type="text/css" href="${contextPath}/resources/css/custom.css">
  • ナビゲーションバー定義(修正版)
    • 定義を読み込む。
<%@ include file="variable.jsp" %>
(略)
<ul class="nav navbar-nav">
  <li><a href="${contextPath}/view/users">一覧を見る<span class="sr-only">(current)</span></a></li>
</ul>
(略)

JSPが評価され統合されたものがtilesで統合されクライアントに返却される。

まとめ

  • その他の定義(scriptやstylesheet)とかは、クライアント側(ブラウザ)で全体を受け取ってから解釈がスタートするので、定義を分離できる。
  • 今回のケースは、サーバサイドで値を評価しなければならないので、各JSPで閉じた定義・処理が必要なよう(たぶん)。
  • 使っても使われるな(;´Д`)

参考

html - Spring MVC Request URLs in JSP - Stack Overflow

*1:そのときの心情である。