argius note

プログラミング関連

とりあえずStruts2+Spring4+MyBatis3の開発環境を作ってみた(2016年初夏バージョン)

きっかけは、Q&Aの対応と、下記の記事です。

Struts(1,2)もSpringもMyBatisも良く知らなかったので、今更Struts2?とも思うのですが、基礎知識として、手始めにStruts2+Spring+MyBatisの環境を構築してみることにしました。


はじめに

今から始めるなら、前述のリンク先の記事にあるようにStruts2を使わずにSpring Bootとかにしたほうが良いと思います。
今回のは、どうしてもこの構成を使いたい人(主に自分)に向けたものです。


Spring4とMyBatis3なのは、Struts2を使う際にSpringとMyBatisの構成を採用される方が多いように見受けられたからです。
で、それぞれの最新バージョンを選択した結果、Struts2+Spring4+MyBatis3になりました。


実際は、かなりの試行錯誤を重ねてここまでたどりついています。
ここまでの設定くらいはカンタンにできるようになっててくれると利用者も増えるのではと思うのですが、もはや古くてマイナーな構成だから仕方ないんですかね。


Struts2のベースはArchetypesのstruts2-conventionを使おうと思ったのですが、余計な部分が多いような気がして、やめました。
結局、maven-archetype-webappをベースにして作ることにしました。


できるだけXMLを書かなくて済むように、アノテーションで設定できるものを選択しています。

...と言いながらもSpringの設定はXMLで書いてしまっていますが、AppContext構成クラスを作っていけばXMLレスも可能のようです。
それぞれの新しいバージョンでは、アノテーションを使ってXMLレスに書けるものが多くなっている印象です。


全部を一気にかくと、どこまでがStruts2でどこまでがSpringでなのかが分かりにくいと思うので、ひとつずつ区切って書いています。



完成版(+α)がGitHubに登録してあります。記事が不要な方、文章を読むのが面倒な方は直接ソースコードを見てください。





環境

基本的に現時点で最新のライブラリーを使うようにしています。

Eclipseについては、私はWindowsなのでPleiades All-in-Oneで確認していますが、Windows以外ならEclipse for JavaEEでも問題ないと思います。

※追記(2016-06-16): 最初の投稿のバージョンでは、Java8の新機能は動作しません。試すの忘れていました。
Struts 2 Java 8 Support Pluginというのを導入する必要があります(asmの除外も必要)。詳細は、後述の「Struts2の設定」を参照して下さい。

  • Struts2 (v2.5)
  • Spring4 (v4.2.6)
  • MyBatis3 (v3.4.0)
  • Tomcat8 (v8.0.26)
  • Pleiades All-in-One (Eclipse 4.5.1)
  • Java8





プロジェクト作成と下準備

下記の設定でMavenプロジェクトを作成します。
この方法だとソースフォルダーで使用するフォルダーがなぜか作成されないので、プロジェクトが作成された後に手動で作成します。

archetype: maven-archetype-webapp
group-id: net.argius
artifact-id: myapp


次に、フレームワークに直接関係ないライブラリーなどの設定を行います。
後で使うので、事前に設定しておきます。


compiler-plugin

Java8にするために、compilerプラグインを設定します。

  <build>
    <finalName>myapp</finalName>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
          <encoding>UTF-8</encoding>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>



プロパティーの設定

バージョンなどのプロパティーを設定します。

  <properties>
    <struts2.version>2.5</struts2.version>
    <springframework.version>4.2.6.RELEASE</springframework.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>



dependencyManagementの設定(Spring)

これが無いと上手く動かなかったので。

下記の記事を参考にしました。

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-framework-bom</artifactId>
        <version>${springframework.version}</version>
        <scope>import</scope>
        <type>pom</type>
      </dependency>
    </dependencies>
  </dependencyManagement>



servlet-apiの追加

最低限のサーブレット設定を追加します。

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
    </dependency>



JDBCドライバーとDB設定

H2でDBアクセスをするので、その準備をします。

JDBCドライバーのライブラリーを追加します。

    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.4.192</version>
    </dependency>


あとはDB接続確認のためにテーブルを作っておきます。


接続設定はapplicationContext.xmlのところで書きますが、

url="jdbc:h2:file:~/start-struts2-spring4-mybatis3;MODE=PostgreSQL;AUTO_SERVER=TRUE"
username="sa"
password="sa"

です。
この設定を使用した場合、ホームディレクトリーの直下にファイルstart-struts2-spring4-mybatis3.*.dbが作成されます。


DBに下記のテーブルを作成してください。
以降のサンプルコードはデータが無くても動作しますので、必要があれば適当に用意してください。

create table users (
  id bigint primary key,
  name varchar(128),
  age smallint
)



※ご存知の方も多いと思いますが、H2のJarファイルをそのまま実行(java -jar h2.jar)すると、ブラウザーベースのコンソール画面が使えます。データベース操作ツールを用意していない方は、このコンソールを利用されると便利です。




Struts2の設定

まずはStruts2の基本ライブラリーを追加します。

アノテーションで設定ができるようにするために、convention-pluginを追加しています。

※追記・訂正(2016-06-16): Java8の新機能を使用するには、Struts 2 Java 8 Support Pluginが必要です。
struts2-java8-support-pluginを追加します。
さらに、coreライブラリーなどが依存しているasmライブラリーは、struts2-java8-support-pluginが参照しているバージョンを使う必要があるので、それ以外のライブラリーではexclusionsasmを除外する必要があります。
今回の例では、struts2-coreに除外設定を入れています。他のasmを参照するライブラリーを使う場合は注意してください。
exclusionsを設定しないと、

java.lang.IncompatibleClassChangeError: class org.apache.struts2.convention.Java8ClassFinder$InfoBuildingClassVisitor has interface org.objectweb.asm.ClassVisitor as super class

という例外が出てActionクラスのロードに失敗します。

    <dependency>
      <groupId>org.apache.struts</groupId>
      <artifactId>struts2-core</artifactId>
      <version>${struts2.version}</version>
      <exclusions>
        <exclusion>
          <artifactId>asm</artifactId>
          <groupId>asm</groupId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>org.apache.struts</groupId>
      <artifactId>struts2-convention-plugin</artifactId>
      <version>${struts2.version}</version>
    </dependency>

    <dependency>
      <groupId>org.apache.struts</groupId>
      <artifactId>struts2-java8-support-plugin</artifactId>
      <version>${struts2.version}</version>
    </dependency>


struts.xmlをクラスパスに追加します。
内容の詳細はここでは触れません。(というかよく分かってません。)必要な方は個別に調べてみてください。

  • src/main/resources/struts.xml

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

  <constant name="struts.enable.DynamicMethodInvocation" value="false" />
  <constant name="struts.devMode" value="true" />

</struts>


web.xmlを追加します。

  • src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="struts_blank" 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">
    <display-name>myapp</display-name>

    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</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>


Actionを追加します。
デフォルトでは、actionsで終わるパッケージに置く必要があるみたいです。
Actionのメソッドは、引数なしの戻り値Stringである必要があります。

結果画面のJSPも合わせて追加します。

  • src/main/java/net/argius/myapp/actions/HelloAction.java

package net.argius.myapp.actions;

import org.apache.struts2.convention.annotation.*;
import com.opensymphony.xwork2.ActionSupport;

public final class HelloAction {

    private String message;

    @Action(value = "/hello", results = { @Result(location = "/WEB-INF/jsp/hello.jsp") })
    public String hello() {
        setMessage("hello!");
        return ActionSupport.SUCCESS;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

  • src/main/webapp/WEB-INF/jsp/hello.jsp

<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body>
<h2><s:property value="message" /></h2>
</body>
</html>


ここまでのsrc以下のファイル構成は以下のようになっています。

src
└─main
    ├─java
    │  └─net
    │      └─argius
    │          └─myapp
    │              └─actions
    │                      HelloAction.java
    │
    ├─resources
    │      struts.xml
    │
    └─webapp
        └─WEB-INF
            │  web.xml
            │
            └─jsp
                    hello.jsp


これでStruts2の最低限の設定が完了しました。

Eclipseからサーバーで実行などでTomcatを起動して、http://localhost:8080/myapp/helloにアクセスすれば、動作を確認できます。




Spring4の追加設定

続いて、Springの設定を追加していきます。

struts2-springプラグインと、Spring4の各種ライブラリーを追加します。
Spring4は、dependencyManagementを設定しているので、個々のバージョン指定は不要です。

    <dependency>
      <groupId>org.apache.struts</groupId>
      <artifactId>struts2-spring-plugin</artifactId>
      <version>${struts2.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
    </dependency>



次に、設定ファイルapplicationContext.xmlを追加します。
冒頭に書いた通り、Javaコードで設定を構成することも可能のようですが、今回はXMLで書いています。Javaで書く方式についてはまた後日調べたら書くかも知れません。


JdbcTemplateは動作確認でを使っているのでbean設定を追加していますが、使わなければこの設定は不要だと思います。

また、いちいちbean設定を書かなくて済むように、component-scanを設定しています。

  • src/main/webapp/WEB-INF/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd">

  <context:annotation-config />

  <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource"
    p:driverClassName="org.h2.Driver"
    p:url="jdbc:h2:file:~/start-struts2-spring4-mybatis3;MODE=PostgreSQL;AUTO_SERVER=TRUE"
    p:username="argius" p:password="argius" />

  <bean class="org.springframework.jdbc.core.JdbcTemplate">
    <constructor-arg ref="dataSource" />
  </bean>

  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>

  <context:component-scan base-package="net.argius.myapp.services" />

</beans>


Webアプリ起動時にSpringコンテキストを有効にするために、web.xmlに設定を追加します。

  • web.xml(差分)

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>


インジェクションするリソースは、サービスとコンポーネントの2通り用意しました。
これだけだとあまり差はありませんが、まだ細かいところが分かっていないので、@Service@Componentの違いくらいです。

  • src/main/java/net/argius/myapp/services/MyCalcComponent.java

package net.argius.myapp.services;

public interface MyCalcComponent {

    int calculate(int a, int b);

}

  • src/main/java/net/argius/myapp/services/MyCalcComponentPlusImp.java

package net.argius.myapp.services;

import org.springframework.stereotype.Component;

@Component("plus")
public final class MyCalcComponentPlusImp implements MyCalcComponent {

    @Override
    public int calculate(int a, int b) {
        return a + b;
    }

}

  • src/main/java/net/argius/myapp/services/MyCalcComponentMinusImp.java

package net.argius.myapp.services;

import org.springframework.stereotype.Component;

@Component("minus")
public final class MyCalcComponentMinusImp implements MyCalcComponent {

    @Override
    public int calculate(int a, int b) {
        return a - b;
    }

}

  • /start-struts2-spring4-mybatis3/src/main/java/net/argius/myapp/services/MyMessageService.java

package net.argius.myapp.services;

import org.springframework.stereotype.Service;

@Service
public final class MyMessageService {

    public String getMessage() {
        return "This is MyMessageService";
    }

}


これらのリソースをDIで利用するActionを書きます。

  • src/main/java/net/argius/myapp/actions/WelcomeAction.java

package net.argius.myapp.actions;

import java.util.*;
import net.argius.myapp.services.*;
import org.apache.struts2.convention.annotation.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.jdbc.core.JdbcTemplate;
import com.opensymphony.xwork2.ActionSupport;

public final class WelcomeAction {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    @Qualifier("plus")
    private MyCalcComponent calcPlus;

    @Autowired
    @Qualifier("minus")
    private MyCalcComponent calcMinus;

    @Autowired
    private MyMessageService messageService;

    private String result;

    @Action(value = "/welcome", results = { @Result(location = "/WEB-INF/jsp/welcome.jsp") })
    public String welcome() {
        List<Map<String, Object>> usersData = jdbcTemplate.queryForList("select * from users");
        setResult(String.format(
            "users: %s, calc-plus: %s, calc-minus: %s, messageService: %s",
            usersData,
            calcPlus.calculate(3, 5),
            calcMinus.calculate(3, 5),
            messageService.getMessage()));
        return ActionSupport.SUCCESS;
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

}

  • src/main/webapp/WEB-INF/jsp/welcome.jsp

<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<body>
<h2>Welcome!</h2>
<p><s:property value="result" /></p>
</body>
</html>


ここまでのsrc以下のファイル構成は以下のようになっています。

src
└─main
    ├─java
    │  └─net
    │      └─argius
    │          └─myapp
    │              ├─actions
    │              │      HelloAction.java
    │              │      WelcomeAction.java
    │              │
    │              └─services
    │                      MyCalcComponent.java
    │                      MyCalcComponentMinusImp.java
    │                      MyCalcComponentPlusImp.java
    │                      MyMessageService.java
    │
    ├─resources
    │      struts.xml
    │
    └─webapp
        └─WEB-INF
            │  applicationContext.xml
            │  web.xml
            │
            └─jsp
                    hello.jsp
                    welcome.jsp


これでSpring4の追加設定が完了しました。

Eclipseからサーバーで実行などでTomcatを起動して、http://localhost:8080/myapp/welcomeにアクセスすれば、動作を確認できます。




MyBatis3の追加設定

最後に、MyBatisの設定を追加していきます。

MyBatisとmybatis-springプラグインのライブラリーを追加します。

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.0</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>


Springの設定にMyBatisの連携設定を追加します。

Springと同様に、mapperを自動スキャンする設定も追加します。
XMLスキーマ宣言の追加が必要なので注意してください。

  • applicationContext.xmlの差分

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
<!--(中略)-->
       xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
       xsi:schemaLocation="
<!--(中略)-->
        http://mybatis.org/schema/mybatis-spring
        http://mybatis.org/schema/mybatis-spring.xsd">

<!--(中略)-->

  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="./WEB-INF/configuration.xml" />
  </bean>

  <mybatis:scan base-package="net.argius.myapp.models" />

</beans>


MyBatisの設定ファイルconfiguration.xmlを追加します。
今回は特に設定することは無いので、内容は空です。

  • src/main/webapp/WEB-INF/configuration.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration />


モデルとマッパーを追加します。
通常、モデルとマッパーのパッケージは分けたほうが良いと思いますが、ここでは簡略化のために同じパッケージに入れています。

マッパーは、XMLを使わずアノテーションで定義しています。

  • src/main/java/net/argius/myapp/models/User.java

package net.argius.myapp.models;

public final class User {

    private long id;
    private String name;
    private int age;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return String.format("User(id=%s, name=%s, age=%s)", id, name, age);
    }

}

  • src/main/java/net/argius/myapp/models/UserMapper.java

package net.argius.myapp.models;

import java.util.List;
import org.apache.ibatis.annotations.*;

public interface UserMapper {

    @Select("select id, name, age from users")
    @Results(value={
            @Result(column="id", property="id"),
            @Result(column="name", property="name"),
            @Result(column="age", property="age")
    })
    List<User> getAllUsers();

}


UserMapperをDIで利用するActionを追加します。

  • src/main/java/net/argius/myapp/actions/UsersAction.java

package net.argius.myapp.actions;

import java.util.List;
import net.argius.myapp.models.*;
import org.apache.struts2.convention.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import com.opensymphony.xwork2.ActionSupport;

public final class UsersAction {

    @Autowired
    private UserMapper userMapper;

    private String result;

    @Action(value = "/users", results = { @Result(location = "/WEB-INF/jsp/users.jsp") })
    public String showUsers() {
        List<User> users = userMapper.getAllUsers();
        setResult(String.format("result: users=%s", users));
        return ActionSupport.SUCCESS;
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

}

  • src/main/webapp/WEB-INF/jsp/users.jsp

<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /></head>
<body>
<h2>Users!</h2>
<p><s:property value="result" /></p>
</body>
</html>


ここまでのsrc以下のファイル構成は以下のようになっています。

src
└─main
    ├─java
    │  └─net
    │      └─argius
    │          └─myapp
    │              ├─actions
    │              │      HelloAction.java
    │              │      UsersAction.java
    │              │      WelcomeAction.java
    │              │
    │              ├─models
    │              │      User.java
    │              │      UserMapper.java
    │              │
    │              └─services
    │                      MyCalcComponent.java
    │                      MyCalcComponentMinusImp.java
    │                      MyCalcComponentPlusImp.java
    │                      MyMessageService.java
    │
    ├─resources
    │      struts.xml
    │
    └─webapp
        └─WEB-INF
            │  applicationContext.xml
            │  configuration.xml
            │  web.xml
            │
            └─jsp
                    hello.jsp
                    users.jsp
                    welcome.jsp


これでMyBatis3の追加設定が完了しました。

Eclipseからサーバーで実行などでTomcatを起動して、http://localhost:8080/myapp/usersにアクセスすれば、動作を確認できます。




おわりに

最終的には至極シンプルな構成に見えますが、イチから始めるとちょっとツラいですね。
とりあえず足がかりにはなる程度のものは用意できたと思います。


はじめにも書きましたが、完成版(+α)をGitHubに登録してあります。
良かったら見てみてください。
もしかしたら、後でGitHubの方だけ修正するかもしれません。




参考URL

など



(おわり)