Struts2とSpringFrameworkをつなぐ方法
Struts2は内部でDIコンテナを持っており、自らが使う設定ファイルから読み込み、指定したクラスのインスタンス化を行えます。DIコンテナの機能は他のDIコンテナに置き換えが可能で、Spring frameworkやJSR-330(Guice)も使えます。それぞれのコンテナごとにStruts2プラグインが用意されていますので、例えばSpring frameworkのDIを利用する場合は、Springプラグインを導入します。
Springプラグインを導入すると、以下の特徴が得られます。
- ActionクラスとValidatorクラスがSpringの管理化に入ります
- これらのクラスからSpringで管理しているBeanへアクセスできます
- Spring frameworkが提供するさまざま機能、Springと他のフレームワークをつなぐ機能も使えます
- 例えば、Spring+Mybatis でDBアクセス
- 宣言的トランザクション管理
- ViewからSpring管理beanへのアクセス
- Spring標準のCache機構 を使ったキャッシュ利用
- Spring+RabbitMQ で 非同期メッセージング
- AOP(AspectJ)の導入
…と、その ほとんどをSpring frameworkとSpringサブプロジェクトに置き換え可能 です。
残念ながらSpringBootの機能までは提供しませんが、Struts2標準のDIコンテナよりも強力な機能を持っていますので、使わない手はありません。対応するSpringのバージョンも、Struts2公式では4.2系までですが、実際のところ4.3系以降も今のところ利用可能です。
導入の仕方
導入の仕方は簡単で、プラグインの入手と、設定の追加をするだけです。
プラグイン
Struts2のバージョンと全く同じStruts2-Spring-pluginを導入します。以下はmavenで指定する場合です。<dependencies>
の子要素に追加します。
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>${struts2.version}</version>
</dependency>
設定
設定ファイルの修正は2か所です。
- Struts2の設定
- web.xml
まずはStruts2の設定の1つである、struts.propertiesに次の行を追加します。場所は任意です。
struts.objectFactory=org.apache.struts2.spring.StrutsSpringObjectFactory
次にweb.xml(デプロイメント・ディスクリプタ)へSpringのコンテキストとリスナーを設定します。以下の設定は、クラスパス/spring/applicationContext.xmlおよび、applicationContext- から始まるxmlファイルをSpringの設定ファイルとして読み込む設定です。
<web-app>
<display-name>prototype_app</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext.xml,classpath:spring/applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
</web-app>
使うための設定は以上です。
この後、Spring管理に入れるBeanたちを登録するapplicationContext.xmlの設定をします。
Actionクラスの実装例
Spring管理下にあるクラスはActionクラスにて@Autowired
して利用できます。以下の例は、簡単な一覧検索のサンプルです。
※コードの一部に Lombok を利用しています。
@Namespace("/")
@Results({@Result(name=ActionSupport.SUCCESS, location="list")})
@Log4j2
public class DisplayListAction extends ActionSupport {
@Action("list")
public String execute() throws Exception {
products = service.search();
log.info("- search:{}" , products);
return SUCCESS;
}
@Autowired
ProductService service;
@Getter @Setter
List<SampleProduct> products;
}
このActionクラスから利用するServiceクラス(ProductService)は次のような固定のリストを返します。
import java.util.Arrays;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
public List<SampleProduct> search() {
List<SampleProduct> resultList =
Arrays.asList(
new SampleProduct().setProduct("A-1", "試作品X-290PA1", 10, true, true),
new SampleProduct().setProduct("A-2", "試作品X-290PA2", 20, true, true),
new SampleProduct().setProduct("B-3", "試作品X-B3", 10, true, true),
new SampleProduct().setProduct("B-4", "試作品X-B4", 30, true, true),
new SampleProduct().setProduct("C-5", "試作品X-C5", 10, true, false),
new SampleProduct().setProduct("C-6", "試作品X-C6", 40, true, false),
new SampleProduct().setProduct("D-7", "試作品X-D7", 10, false, true),
new SampleProduct().setProduct("D-8", "試作品X-D8", 50, false, true),
new SampleProduct().setProduct("E-9", "試作品X-E9", 10, true, true),
new SampleProduct().setProduct("Z-1000", "試作品Z-1000", 0, false, false)
);
return resultList;
}
}
ここで登場するSampleProductは単純なDataTransferObjectです。検索結果なのでValueObject化しても良いですね。
Springのbean設定:applicationContext.xml
残念ながらJavaConfigは使えませんので、xmlファイルで定義します。component-scanは使えますので、beanを一気に登録できます。
<?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"
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"
default-autowire="byName"
> <!-- Spring管理になるServiceクラス定義 -->
<context:component-scan base-package="seren.struts2.sampleapp.service" use-default-filters ="false" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
</beans>
component-scanの対象を、base-packageにある @Service
アノテーションが付いているクラスを登録しています。
設定は以上になります。一度作っちゃえばカンタンですね v(・ω・|
サンプルプロジェクトをgithubで公開しています
おまけ
Struts2からThymeleaf3も使えます。Struts2のタグは一切使えませんが、Thymeleafの方がViewの記述がしやすいでしょう。Struts2からSpring frameworkなどに切り替える場合にはとても重宝するでしょう。