7.

Struts1 カスタムタグ (taglib) 完全ガイド (bean/html/logic/nested)

編集
この記事の要点
  • カスタムタグ (taglib) = JSP で独自タグを使う仕組み。Struts1 では bean / html / logic / nested の4つの TLD が標準提供
  • 使うには JSP 先頭で <%@ taglib uri="..." prefix="..." %> を宣言
  • 主要タグ: bean:write (値出力)、html:form / html:text / html:submit (フォーム生成)、logic:iterate / logic:equal (制御)、nested (ネスト型 ActionForm)
  • 独自タグを作る場合は TagSupport を継承し doStartTag() / doEndTag() を実装、TLD に登録
  • Struts1 は 2013 年 EOL。新規開発は Spring MVC + JSP / Thymeleaf、もしくは Struts2 へ移行を推奨

カスタムタグとは

JSP では HTML タグ風の独自タグを定義して、Java コードを書かずに動的処理を行えます。これをカスタムタグと呼び、関連するタグをまとめた XML 定義ファイルが TLD (Tag Library Descriptor)、それを JSP から使えるようにする宣言が <%@ taglib %> ディレクティブです。

Struts1 では、フォーム生成 / 値出力 / ロジック制御を行うために、以下 4 つの標準タグライブラリが配布されています:

TLD用途URI
beanBean プロパティ参照http://struts.apache.org/tags-bean
htmlHTML フォーム生成http://struts.apache.org/tags-html
logic条件分岐 / 繰り返しhttp://struts.apache.org/tags-logic
nestedネスト型 Bean 操作http://struts.apache.org/tags-nested

taglib ディレクティブの書き方

JSP の先頭に宣言します。uri は TLD で定義された URI、prefix は JSP 内で使う接頭辞です。

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://struts.apache.org/tags-bean"   prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html"   prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic"  prefix="logic" %>
<%@ taglib uri="http://struts.apache.org/tags-nested" prefix="nested" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core"    prefix="c" %>

WEB-INF/lib/struts.jar 内の META-INF/tlds/ に各 TLD が同梱されており、URI からそのまま解決されます。古い構成では WEB-INF/struts-html.tld を直接 URI 指定するパターンも残っています。

bean タグ (値出力 / 取得)

<%-- リクエスト属性 user.name を HTML エスケープして出力 --%>
<bean:write name="user" property="name" />

<%-- HTML エスケープしない (XSS 注意) --%>
<bean:write name="user" property="bio" filter="false" />

<%-- 日付フォーマット --%>
<bean:write name="user" property="createdAt" format="yyyy/MM/dd HH:mm" />

<%-- メッセージリソースから取得 --%>
<bean:message key="label.username" />

<%-- スコープ変数を新規定義 --%>
<bean:define id="total" value="<%= request.getAttribute("count") %>" />
${total}

html タグ (フォーム生成)

<%-- ActionForm "loginForm" にバインドされたフォーム --%>
<html:form action="/login" focus="username">

  <%-- ActionForm.getUsername() / setUsername() に紐付く --%>
  Username: <html:text property="username" size="20" maxlength="50" />

  Password: <html:password property="password" size="20" />

  <%-- チェックボックス --%>
  <html:checkbox property="rememberMe" /> Remember me

  <%-- セレクトボックス (options タグでループ) --%>
  <html:select property="country">
    <html:options collection="countries" property="code" labelProperty="name" />
  </html:select>

  <%-- メッセージリソース連動の submit --%>
  <html:submit><bean:message key="button.login" /></html:submit>

  <%-- エラーメッセージ表示 --%>
  <html:errors />

</html:form>

生成される HTML 例:

<form name="loginForm" method="post" action="/app/login.do">
  <input type="text" name="username" maxlength="50" size="20" value="" />
  <input type="password" name="password" size="20" value="" />
  <input type="checkbox" name="rememberMe" value="on" />
  <select name="country">
    <option value="JP">Japan</option>
    <option value="US">United States</option>
  </select>
  <input type="submit" value="Login" />
</form>

logic タグ (条件分岐 / 繰り返し)

<%-- ユーザがログイン済の場合のみ表示 --%>
<logic:present name="user">
  Welcome, <bean:write name="user" property="name" />
</logic:present>

<%-- 逆: 未ログインのみ --%>
<logic:notPresent name="user">
  <a href="/login.do">Login</a>
</logic:notPresent>

<%-- 値の比較 --%>
<logic:equal name="user" property="role" value="ADMIN">
  <a href="/admin.do">Admin Panel</a>
</logic:equal>

<%-- リストの繰り返し --%>
<table>
  <logic:iterate id="item" name="items" type="com.example.Item">
    <tr>
      <td><bean:write name="item" property="id" /></td>
      <td><bean:write name="item" property="name" /></td>
      <td><bean:write name="item" property="price" /></td>
    </tr>
  </logic:iterate>
</table>

nested タグ (ネスト型 Bean)

ActionForm のプロパティが「Bean のリスト」のような場合に、ネストを意識せず書ける糖衣構文。

<%-- itemsForm.items[i].name にバインド --%>
<nested:form action="/save">
  <nested:iterate property="items">
    <nested:text property="name" />
    <nested:text property="price" />
  </nested:iterate>
  <html:submit value="Save" />
</nested:form>

<%-- 内部で items[0].name / items[1].name ... と展開される --%>

独自カスタムタグの作り方

標準で足りないときは自作できます。TagSupport を継承し、doStartTag() / doEndTag() を実装、TLD で宣言します。

package com.example.tags;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.IOException;

public class GreetingTag extends TagSupport {

    private String name;

    public void setName(String name) { this.name = name; }

    @Override
    public int doStartTag() throws JspException {
        try {
            pageContext.getOut().print("Hello, " + name + "!");
        } catch (IOException e) {
            throw new JspException(e);
        }
        return SKIP_BODY;
    }
}

TLD ファイル (WEB-INF/example.tld):

<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        version="2.0">
  <tlib-version>1.0</tlib-version>
  <short-name>ex</short-name>
  <uri>http://example.com/tags</uri>

  <tag>
    <name>greeting</name>
    <tag-class>com.example.tags.GreetingTag</tag-class>
    <body-content>empty</body-content>
    <attribute>
      <name>name</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>
</taglib>

JSP から使用:

<%@ taglib uri="http://example.com/tags" prefix="ex" %>
<ex:greeting name="Taro" />
<%-- → Hello, Taro! --%>

JSP 2.0 タグファイル (Tag File)

Java クラスを書かずに JSP だけでカスタムタグを定義できる仕組み (JSP 2.0+):

<%-- WEB-INF/tags/card.tag --%>
<%@ tag pageEncoding="UTF-8" %>
<%@ attribute name="title" required="true" %>
<%@ attribute name="color" required="false" %>

<div style="border:1px solid ${color}; padding:1em">
  <h3>${title}</h3>
  <jsp:doBody />
</div>
<%@ taglib tagdir="/WEB-INF/tags" prefix="my" %>

<my:card title="お知らせ" color="#0066cc">
  これは本文です。
</my:card>

JSTL との関係

JSTL (JSP Standard Tag Library) は標準化されたタグライブラリで、Struts1 の logic タグと多くが重複します。JSTL の方が一般的で長期保守されるため、新規 JSP は JSTL を推奨:

Struts logicJSTL c タグ
logic:iteratec:forEach
logic:equalc:if test="${var == 'x'}"
logic:presentc:if test="${not empty var}"
bean:writec:out value="${var}" or ${var}

Struts1 移行ガイド

Apache Struts1 は 2013 年 4 月 5 日に EOL (End of Life) を迎えており、セキュリティパッチが提供されません。本番システムでまだ使っている場合は段階的移行を強く推奨:

  • Spring MVC + Thymeleaf / JSP — 最も普及、エンタープライズ向け
  • Struts2 — Struts1 とは別物の OGNL ベース、移行コストは大きい
  • Spring Boot + REST + React/Vue — SPA 化を視野に入れるなら

FAQ

Q: tablib と taglib どちらが正しい?
A: 正しくは taglib。「tablib」は誤記が広まったものです。

Q: カスタムタグと EL (Expression Language) はどう違う?
A: EL ${var} は値の取得のみ、カスタムタグはタグとして処理ロジックを内蔵します。両者は併用できます。

Q: TLD が見つからないエラーが出る
A: org.apache.jasper.JasperException: This absolute uri ... cannot be resolved は struts.jar が WEB-INF/lib にない / web.xml の taglib マッピング不一致が典型原因です。

編集
Post Share
子ページ
  1. struts-bean.tld
同階層のページ
  1. 導入方法
  2. struts-config.xmlの説明
  3. ActionForm
  4. Action
  5. エラー一覧
  6. JSP タグ
  7. カスタムタグ(tablib)

最近更新/作成されたページ