とりあえずStruts2+Spring4+MyBatis3の開発環境を作ってみた(2016年初夏バージョン)
きっかけは、Q&Aの対応と、下記の記事です。
- 3つのフレームワークで学ぶエンタープライズJava開発入門(1):Strutsを使い続けることの問題点&現在有力なJava EE、Spring、Play Frameworkの基礎知識とアーキテクチャ - @IT
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
が参照しているバージョンを使う必要があるので、それ以外のライブラリーではexclusions
でasm
を除外する必要があります。
今回の例では、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 />
モデルとマッパーを追加します。
通常、モデルとマッパーのパッケージは分けたほうが良いと思いますが、ここでは簡略化のために同じパッケージに入れています。
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の方だけ修正するかもしれません。