Struts 2でもさわってみるか その1

最近のJavaフレームワークがどうなっているか調べるために、Strutsの後継である「Struts 2」をさわってみた。

Struts 2」はStrutsに「WebWork」というフレームワークをマージしたもの。「Struts 1」と似てはいるけど、別物と考えた方がいい。

最新のバイナリを以下のサイトからダウンロードしてきて使う。現在のバージョンは「2.1.6」

開発環境には、

を使う。

とりあえず簡単なTodoアプリでも作ってみる*1

作ってみる

Tomcat プロジェクト」で新規プロジェクトを作る。プロジェクト名は「jTodo」にする。

Struts 2の開発に必要なJarファイルを「WEB-INF/lib」フォルダにコピーしておく(以下は必須のJarファイル)。

  • commons-fileupload-1.2.1.jar
  • commons-logging-1.0.4.jar
  • freemarker-2.3.13.jar
  • ognl-2.6.11.jar
  • struts2-core-2.1.6.jar
  • xwork-2.1.2.jar

あと、楽をする為にConventionプラグインを使うので以下のJarファイルもコピーしておく。

  • struts2-convention-plugin-2.1.6.jar

struts2-core-2.1.6.jar」をビルドパスに追加しておく。

まずはデプロイメントデスクリプタ(web.xml)から。

WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://
                     java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher
        </filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>

次はStrutsの設定ファイル(struts.xml)、別に無くてもいい(Conventionプラグイン使うから)。

WEB-INF/src/struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
	<constant name="struts.devMode" value="true"/>
	<constant name="struts.convention.classes.reload" value="true" />
</struts>

とりあえず、Todoアイテムの情報を格納するクラスを作っておく。

com.coma2n.jtodo.TodoItem.java

package com.coma2n.jtodo;

public class TodoItem {
    private int id;
    private int priority;
    private String title;
    
    public void setId(int id) {
        this.id = id;
    }
    public int getId() {
        return id;
    }
    public void setPriority(int priority) {
        this.priority = priority;
    }
    public int getPriority() {
        return priority;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getTitle() {
        return title;
    }
    
    public TodoItem() {
    }
    public TodoItem(int id, int priority, String title) {
        this.id = id;
        this.priority = priority;
        this.title = title;
    }
}

プロパティとしてIdPriorityTitleを定義しておく。

まずはTodoアイテムの一覧を表示する画面(初期画面)から作る。ビューにはJSPを使う*2

WEB-INF/content/index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>jTodo</title>
    <style type="text/css">
        #datagrid {
          background-color: silver;
        }
        #datagrid th {
          background-color: blue;
          color: white;
          padding: 4px;
        }
        #datagrid td {
          background-color: white;
          padding: 2px;
        }
    </style>
</head>
<body>
    <table id="datagrid" border="0" rules="all">
        <thead>
           <tr>
               <th>優先度</th>
               <th>タイトル</th>
           </tr>
        </thead>
        <tbody>
        <s:iterator value="#session.ITEMS" var="item">
           <tr>
               <td align="center"><s:property value="#item.Priority" /></td>
               <td><s:property value="#item.Title" /></td>
           </tr>
        </s:iterator>
        </tbody>
    </table>
</body>
</html>

解説

Strutsタグのタグライブラリをインポートしている。

<%@taglib prefix="s" uri="/struts-tags" %>

iteratorタグでリスト要素に対して処理を行える。ここではvalue属性にセッション*3から取り出した「ITEMS」というキーのオブジェクトをリスト要素として渡し、その各要素をvar属性で「item」と宣言している。

当たり前だけど、現時点ではセッションに何も格納されていない。

<s:iterator value="#session.ITEMS" var="item">
...
</s:iterator>

そして、その中ではpropertyタグを使ってオブジェクトのプロパティを出力している。

<tr>
   <td align="center"><s:property value="#item.Priority" /></td>
   <td><s:property value="#item.Title" /></td>
</tr>

まぁ簡潔って言えば、簡潔かな。前とあんまり変わってないね。

で、次はこのページにデータを送るためのコントローラ(Strutsではアクション)を実装するんだけど、「Struts 1」ではActionSupportとかいうクラスを継承してクラスを作って、「struts.xml」にマッピングを追加して〜とか、なんか一画面追加するためだけでも色んな事をする必要があったんだけど、「Struts 2」ではその辺がだいぶ楽になっている。

といっても、Conventionプラグインを使わないと以前と同じようにやるか、アノテーションに頼る事になる。

コンベンションプラグインを使うとどうなるかと言うと、その名が示す通りアクションクラスを規約に従って作れば、自動的にビューと関連付けてくれるというもの。

その規約の条件は、

  • クラス名を「***Action」とする*4
  • 関連づけるビューは「***Action」の「***」部分を同じにする。
  • 「public String execute」というシグネチャのメソッドを実装する*5
  • 二階層より下のパッケージにクラスを含める*6
  • ビューは「WEB-INF/content」フォルダに含める*7

となっている。

ということで、「index.jsp」に対応するアクションクラスの「IndexAction」を実装する。

com.coma2n.jtodo.actions.IndexAction.java
package com.coma2n.jtodo.actions;

import java.util.Map;
import java.util.ArrayList;

import org.apache.struts2.interceptor.SessionAware;

import com.coma2n.jtodo.TodoItem;

public class IndexAction implements SessionAware {
    private Map<String, Object> session;
    
    @Override
    public void setSession(Map<String, Object> session) {
        this.session = session;
    }
    
    @SuppressWarnings("unchecked")
    public String execute() {
        ArrayList<TodoItem> list = (ArrayList<TodoItem>)session.get("ITEMS");
        if(list == null) {
            list = new ArrayList<TodoItem>();
            
            session.put("ITEMS", list);
        }
        return "success";
    }
}
解説

今回、Todoアイテムの情報を外部に永続化するのが面倒くさかったのでメモリ中でやる事にした。

Struts 2」ではセッション情報にアクセスしたいクラスでSessionAwareインターフェースを実装してやれば、自動的にセッション情報のMapオブジェクトが渡されるようになっている*8

executeメソッドでは、セッションから「ITEMS」のキーでオブジェクトを取得して、存在しなければ新しくインスタンス化して設定しているだけ。

このexecuteメソッドというのは、ASP.NETの「Page_Load」イベントハンドラのような役割をもっているらしい。

これで「http://localhost:8080/jTodo」にアクセスして、データが入っていれば(入ってるわけがないけど)以下のように表示される。

うん、「Struts 1」に比べてだいぶ楽になっている。

続きはまた今度

ソース

*1:俺、こればっかりやなorz

*2:Struts 2ではVelocityや他のビューを簡単に切り替える事ができる

*3:Mapオブジェクト

*4:ActionSupportから継承すればいらない

*5:いらんかもしれん

*6:設定で変更できる

*7:設定で変更できる

*8:この辺、Springの影響をもろに感じる