2014 年には Struts の脆弱性が見つかり、Java 界隈に大きな衝撃を与えました。

私はそんなタイミングで Spring Framework と出会い、実際に 1 案件で使ってみたところ、「 使えるフレームワーク 」という結論に至りました。

サンプルを交え、 Spring Framework の特徴をご紹介いたします。


バージョンは Spring 4.0 です。

Web アプリケーションの開発のため、

  • Spring Web
  • Spring Web MVC
  • Spring ORM

あたりを主に使用しました。

Spring Boot や Spring Roo といったプロジェクト生成の補助ツールや Spring Security は未使用です。
Spring Android も No Check です。

オフィシャル

Spring Framework Official
Spring Framework Official
http://spring.io/

オシャレですね。
そして、しっかりレスポンシブします。

Spring Framework はフルスタックなフレームワークではなく、コンポーネントの集合体から成っています。
そのためプロジェクトの数が多く、「あれをやるのに何が必要か」を把握するまでのとっつきにくさは否めません。

そんな時は公式のガイドページからサンプルをチェックしてみましょう。

Guides
http://spring.io/guides

ガイドの点数が豊富で、例えば以下のようなトピックがあります。

  • Building a RESTful Web Service
  • Accessing Relational Data using JDBC with Spring
  • Registering an Application with Twitter
  • Validating Form Input
  • Spring Boot with Docker

ガイドの詳細ページでは所要時間、環境、 Maven の pom.xml など記載があり、使いやすいですね。
ただし、 英語 ですけれど。

STS

Spring Framework は、 Spring Tools として、次の IDE 用のプラグインを提供しています。

  • Eclipse
  • Visual Studio Code
  • Theia

次から入手できます。

Spring Tools
https://spring.io/tools

以前は、 STS ( Spring Tool Suite ) といって、 Eclipse を Spring 開発向けにカスタマイズしたものが提供されていましたが、現在はプラグイン形式に変わったようですね。

Spring Framework の特徴

  • コンポーネントの集合体
  • アノテーション・ドリブン
  • DI
  • AOP
  • REST

アノテーション・ドリブン

Java といえばアノテーションです。

Web 開発では、spring-webmvc に @Controller@RequestMapping など必須アノテーションが提供されています。

一口にクラスといっても、 Controller、 Model、 View と様々な役割がありますが、アノテーションによって 役割を明示し、ふるまいを定義する ことができ、プログラムの構成単位が分かりやすくなります。

また、アノテーションの自作も可能です。
例として、こんなことをしていました。

  • API は @isApi というアノテーションで装飾する
  • ログイン不要なページには @NoLogin をつけておく

DI

DI とは 依存性の注入 といわれるもので、 Spring といえば DI というくらい DI する場面が多かったです。
それは各コンポーネント間を疎結合にし「外部の設定ファイルなどを用いてコンポーネントを注入する仕組み」です。

Spring で提供される DI コンテナ が、インスタンスの生成、初期化、破棄などを管理します。
DI コンテナにクラスのインスタンスを要求すれば使えるようになるのです。
理解が進むと、大変便利な機能です。

求めよさらば与えられん。

例:クラスを DI する

  • servlet-context.xml
1<context:annotation-config />
2<context:component-scan base-package="com.example.controller" />

準備として、 com.example.controller 以下のクラスをすべて DI できるように設定を行います。

  • ExampleServoceImpl.java
 1@Controller
 2public class ExampleController {
 3
 4    @Autowired
 5    private ExampleService exampleService ;
 6
 7    @RequestMapping('/')
 8    @ResponseBody
 9    public String find() {
10        List<ItemEntity> items = exampleService.findAllItems();
11        
12    }
13}

ExampleService に @Autowired アノテーションをつけ DI します。
こうするだけで new せずに、オブジェクトを操作することができるんですね。


Autowired は便利ですが、クラスの冒頭で 100 も Autowired しているようなものはやりすぎなので、そのあたりの設計はうまいことやりたいな、という感じでした。

また、サンプルでは Controller を起点にしています。
こうした場合、Controller で New したインスタンスの中に Autowired が宣言されていたとしても、DI されずに NULL だったということがありました。

例:プロパティファイルをDIする

PropertiesFactoryBean を使い、 properties の DI を行うことが可能です。

詳しくは以下をご覧ください。

[Java][Spring]Propertiesクラスの使い方
SpringFrameworkでPropertiesクラスの使い方を勉強してみました。
Atuweb 開発 Log

AOP

「横断的関心事の分離( Separation Of Cross-Cutting Concerns )」といいます。

後から機能や処理を差し込む、フィルターのようなものという解釈で OK でしょう。

実装例

  • servret-context.xml
1    <aop:aspectj-autoproxy proxy-target-class="true" />
2    <context:component-scan base-package="com.example.game.web.annotation" />
3    <beans:bean id="MaintenanceAspect" class="com.example.game.aspect.MaintenanceAspect" />
4    <beans:bean id="LoginAspect"       class="com.example.game.aspect.LoginAspect" />

aspectj-autoproxy を宣言し、 AOP を有効にします。
また、Aspect のクラスを XML 内に定義します。

  • MaintenanceAspect.java
 1@Aspect
 2public class LoginAspect implements Ordered {
 3
 4    // 指定のアノテーションに対してcheckLogin()実行する
 5    @Pointcut(
 6        "!@annotation(com.example.game.annotation.NoLogin) && "+
 7        " @annotation(org.springframework.web.bind.annotation.RequestMapping)"
 8    )
 9    public void checkLogin() {
10    }
11
12    // checkLogin()の前後に処理を挟む
13    @Around("checkLogin()")
14    public Object execCheckLogin(ProceedingJoinPoint pjp) throws Throwable {
15        // ログイン処理
16        return pjp.proceed();
17    }
18
19    @Override
20    public int getOrder() {
21        return AspectOrder.LOGIN.ordinal();
22    }
23}

こうすること「 @RequestMapping が付加され、かつ @NoLogin が付加されていない」メソッドを実行するときポイントカットが働きます。

処理順序を制御するためには getOrder() をオーバーライドしてください。
AspectOrder は処理順を保証するための次の Enum を用意し、 getOrder() 内から呼び出します。

  • AspectOrder.java
1public enum AspectOrder {
2    MAINTENANCE,
3    LOGIN;
4}

サーブレットフィルタですと、処理順序を制御するのが難しい印象があるのですが、こちらはシンプルです。

REST

REpresentational State Transfer

これは「 HTTP メソッド (GET、POST、PUT、DELETE など) でリソースを操作する」ものと認識しています。

最近の Web フレームワークは RESTful なものが多いですね。
Spring を採用した案件が API ベースのため、あまり意識する場面はありませんでした。

 1import static org.springframework.web.bind.annotation.RequestMethod.GET;
 2import static org.springframework.web.bind.annotation.RequestMethod.POST;
 3
 4@Controller
 5public class WebViewController {
 6
 7    @RequestMapping(value = "/serial", method = GET);
 8    public String showSerialForm (Model model) {
 9    	return "/serial/index"
10    }
11
12    @RequestMapping(value = "/serial", method = POST);
13    public String inputSerial (
14        Model model,
15        @RequestParam(required = true) String code
16    ) {
17        // シリアルコードの検証処理
18    	return "redirect:/serial"
19    }
20}

どちらも /serial にマッピングしていますが、 GET/POST ごとに受け取るメソッドがことなることが、おわかりになると思います。

API の実装ではメソッドを「 POST のみ」に制限していましたが、 method = POST するだけでしたので非常に簡単でした。

ちなみに、POST のみに制限された URL へ、GET でアクセスした場合は HTTP status 405 method not allowed がかえります。

まとめ

Java にはディファクトスタンダードがないと言われますが、Spring Framework はその候補と言えるフレームワークではないでしょうか。

pom.xml や web.xml を設定することは正直大変でしたが、慣れると各コンポーネントの結合度が浅く使いやすいと感じる場面が多かったですね。
そして、 JavaVM 上で動作しますので、一定のメモリ消費はありますが、高速に動作します。

[Java]PHPオンリーでJava初心者だった私がSpring Webの開発でつまづいた点を振り返る
Javaの経験が浅い私が、SpringFrameworkを使った開発で困ったことなどをまとめました。
Atuweb 開発 Log

関連書籍

ざっくり読みました。
Spring の時代背景、AOP や DI について詳しめに説明しており、Springを少し触った後に読むと理解が進むという印象を受けました。

Java を理解していることが前提であり、プログラミング初心者向けではありません。


その他、比較的新しい書籍としては次のものがあるようです。