Springとは
Spring Bootを紹介する前に、Spring Bootの基盤技術でもあるSpring Frameworkについて簡単に紹介します。
Javaを以前から使っているならば、Springという言葉を聞いたことがある方も多いはずです。Strutsなどが全盛期の頃は、Springと言えば現在のSpring Frameworkを指していました。そのため、現在でもSpring Frameworkのことを単にSpringと呼ぶ方もいます。
Spring "Framework"と呼ばれてはいますが、開発者にとってはフレームワークというよりも、DI(依存性注入)やAOP(アスペクト指向プログラミング)といったスタイルを実現するためのライブラリに近い位置づけかと思います。
そのため、Spring Frameworkはどのようなフレームワークを選んでいても、よく一緒に使われてきました。
現在でこそDIスタイルのプログラミングはスクリプト言語でもよく見かけますが、DIやAOPといったトレンドが生まれた大きな要因はJavaであり、そのJavaの中で強く影響力を与えた1つがSpring Frameworkです。
そしてその後、Spring Frameworkは表1に示す通りさまざまなニーズに応じてプロジェクトを増やし、多くの開発者が利用するようになってきました。
| プロジェクト名 | 概要 |
|---|---|
| Spring Framework | Springプロジェクトの基本となるプロジェクト。DIやAOPなどを実現するためのフレームワーク。 |
| Spring Cloud | クラウド環境を前提としてアプリケーションを分散構成として管理するためのプロジェクト。 |
| Spring Data | ORマッピングなどの各種のRDBやNoSQLなどデータストアを行うためのプロジェクト。データストアごとにサブプロジェクトに分かれているので、実際に利用するミドルウェアごとに使い分ける。 |
| Spring Integration | さまざまなシステムやサービスを疎結合につなげてシステム全体を構築するようなEIP(Enterprise Integration Patterns)フレームワークのプロジェクト。また、このようなシステムをEIP(Enterprise Integration Patterns)とも呼ぶので、EIPフレームワークといった呼ばれ方もします。 |
| Spring Batch | バッチ処理を行うシステムを構築するためのプロジェクト。 |
| Spring Shell | 独自のShellコンソールを持つためのプロジェクト。例えば、JSehllのようなREPL(Read-Eval-Print Loop)ツールのようなインターフェースを持つアプリケーションの作成が可能。 |
| Spring Security | システムの「認証」や「認可」を行うセキュリティ用機能を実現するためのプロジェクト。 |
| Spring AMQP | AMQP(Advanced Message Queuing Protocol)を扱うためのプロジェクト。JMSと似ているがAMQPというプロトコルを扱う点が異なる。AMQPを扱えるプロダクトはいくつかあり、Rabbit MQなどがある。 |
| Spring Web | Services SOAPやXMLなどを用いたWebServiceを構築するためのプロジェクト。 |
また、これ以外にも多くのプロジェクトがあります。詳しくはこちらの公式ページを参照してください。
[Note:EIP(Enterprise Integration Patterns)フレームワークとは]
システム間の接続をメッセージング形式で定義し、それらの入出力エンドポイントとして接続することでシステム全体を構築するためのフレームワーク。例えば、FTPやSCP、メール受信などを他のシステムの入力として簡単に接続ができる。Javaで有名なプロジェクトとしてはSpring Integrationの他にApache Camelがある。
Spring Frameworkの歴史と背景
Spring Frameworkの1.0がリリースされたのは2004年です。この頃にはEJBも2.0もリリースされエンタープライズでのJavaの利用シーンも広がっていました。
しかし、多くの方にとってEJBは過剰なシステムであり、依然としてTomcat等のWebアプリケーションコンテナ上にアプリケーションを構築するケースが多い時代でした。その中で蓄積してきたコード資産や外部のライブラリなどを、効率よく利用できる仕組みが求められ、管理を最適化する需要が高まりました。さらに、実際よりも高度な機能を求めてしまっているEJBなどエンタープライズ向けのフレームワークを簡素化したい需要も生まれました。
その両者に応えるようにその存在感を増していったのがSpring Frameworkです。
Spring Frameworkの特徴
Spring Frameworkの特徴としてよく上げられるのが、AOP(アスペクト指向プログラミング)とDI(依存性注入)です。
まずAOPとは何かを簡単に説明するならば、「コード自体に変更を加えることなく、新たな処理をそのコードの実行時に加えること」です。しかし、AOPはビジネスロジックを作る開発者の間ではほとんど使われていないのが現状でしょう。
よくある説明ではログ処理などを加えるものがありますが、その程度であればAOPを使ってまで実装する必要はありません。もちろん、AOPのようなプログラミングスタイルは、汎用的なライブラリやフレームワーク開発者は知っておくべき知識であり、知っておいた方が便利ではありますが、Spring Bootを利用する上で必須の知識ではなく、どうしても必要になった時点での理解でも間に合うと思います。そのため、ここでは説明は省略します。
一方で、DIの概念は非常に一般的なものになってきています。もともとDIは、Javaなどのコンパイル言語で実行する際、オブジェクトの生成処理などを変えられない場合に、大きな効果を発揮していました。しかし現在ではスクリプト言語でも一般的に使われるようになり、「疎結合な設計」を実現するコーディングスタイルとして、DIの仕組みが使われるようになってきています。
Spring FrameworkのDI機能
Spring FrameworkのDIを定義する方法には、以下の3つがあります。
- XMLファイルを使い定義する方法(リリース当初から利用可能)
- Javaのソースコード内で定義する方法 (Spring Framework 3.0から利用可能)
- アノテーションを使い定義する方法(Spring Framework 2.5から利用可能)
Spring Bootを使う場合にはJavaのコードで定義する方法か、アノテーションを使って定義する方法がよく使われます。次回以降で紹介するサンプルではこれらの方法を用いていますが、今回はXMLを使った方法について簡単に説明します。
Spring Frameworkを使うプロジェクトはSpring Bootが初めて、という開発者の中には、XMLを使った方法があることを知らない方もいるようです。
XMLを利用する場合には、リスト1のように定義していきます。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" // ・・・省略・・・・ <bean id="main.handler" class="com.coltware.server.WebRTCSignalingWebSocketHandler" scope="prototype"> <property name="channelGroup"> <ref bean="channel.group"></ref> </property> </bean> <bean id="channel.group" class="com.coltware.channel.AllChannelGroup"> </bean> </beans>
ここでは、XMLで設定する場合の課題点を理解してもらうためにコード例を示しているため、具体的なコードの説明はしませんが、複雑なことをしようとすればするほどXMLも複雑になっていき管理が難しくなることがイメージできると思います。
XMLファイルは分割して管理もできますが、設定が大きく複雑になればなるほど、その設定が正しいかどうかも判別しづらくなり、利用しているコード部分の担当開発者でないと分からない状況になりがちです。このため、コンパイルが必要なJavaであってもXMLでの設定からだんだんと、ソースコード内に定義できる方法が広まっていったと思われます。
また、ソースコード内に定義が移っていったことにより、柔軟に定義でき、分割して管理することも容易になっていきます。
ただし、データベースの接続ユーザやパスワードを設定する場合にはソースコードベースであっても設定ファイルから簡単に値を取得するようになっていますので、その点は心配する必要はありません。
DIコンテナの必要性
設計時における「疎結合」の実現だけであれば、DIコンテナを使わずとも実現は可能です。また、運用時に後から利用するオブジェクトを変える必要性が生じるケースはほとんどないと思います。また、その必要性が生じた場合でも、その前提としてビジネスロジックに関しての機能の改善や修正なども求められるケースがほとんどであり、やはり、運用時にDIコンテナの必然性を疑問に思う方もいるでしょう。
それにもかかわらずDIコンテナは既になくてはならない要素の1つになっています。そこには現在の開発スタイルが大きく影響していると思われます。
現在、開発者にとって結合テストやサービス全体の全体テストよりも、単体テストの必要性がより重視される傾向が強まっています。
その背景には、以下の2つの理由があると筆者は思います。
- アプリケーションフレームワークを前提とした開発スタイルの普及
- 外部サービスAPIの共通化
アプリケーションフレームワークは、一般的に、それぞれの機能を自分で作成できる開発者であっても利用します。アプリケーションフレームワークを用いることで、実際の動作時のリソースやセキュリティ面などを考慮した実装を省略できるからです。
こういった利点は、開発期間を短くするだけでなく、品質の安定性やノウハウの蓄積などにも大きく寄与しています。一方でフレームワークを使う前提の場合、実装されたビジネスロジック部分をテストするときも、フレームワーク上で動作させてチェックしなくてはなりません。
すべての開発者がアプリケーションフレームワーク上でテストできるプロジェクトであれば問題ありませんが、規模が拡大した場合や、さまざまな条件下で参加している場合など、テストできない開発者が出てきてしまうケースがあります(図1)。
そこで、DI機能により、実際の本番運用や結合テストのように全体を起動させる前提の場合と、単体テストやユニットテストのように一部分だけでテストをする場合に、その前提を変えることができる仕組みになっています。
通常のWebシステムで動作確認をする場合には、その確認自体の処理の前にもいくつかの条件があります。例えば、既にログインされ認証済みであることや、POSTデータとしてサーブレットから入力データを取得する場合には、そのサーブレットが起動しているWebコンテナ自体が必要になります。
しかし、単体テストでは、これらの条件を毎回満たした上でテストする必要があるので手軽にテストができなくなります。そこでDIを利用することで、認証後のセッション情報の取得やサーブレットから必要なデータがあたかも取得できる状態としてテストできるようになるのです。
現在では、アプリケーションフレームワーク自体にテストのための機能が含まれている場合も多いので意識しなくなっていますが、DIの機能が普及する前は単体テストをするにも非常に面倒な手順を毎回行うことが多かったです。
こうした傾向は、システム上必要な普遍的な機能だけでなく、外部のサービスを使う場合でも高まっています。図2のようにアプリケーションを作成する際に外部サービスと接続することが当たり前になってきました。これらの外部サービスは有料であることも多く、すべての開発者がそのサービスを使えるケースもなかなかないのが現状です。
そのため、結合テストの段階になるまで、実際のサービスに接続できないケースもあります。そうした中で開発するためには、実サービスと接続する場合と同じテスト用のモックが必要です。
また、現在ではOAuthのように異なるサービスであっても、同様に利用できるようインターフェースが定義されたり、共通のインターフェースがなくてもそれらの違いをライブラリが吸収したりするケースが一般化しつつあり、そうすることでテスト自体を省略することもあります。
DI機能は、こういったケースでも有効に機能します。
Spring Bootとは(1)
DI機能を使えば疎結合な実装が作りやすくなるため、外部のライブラリやフレームワークなどの導入が簡単になります。ただし、さまざまなプロジェクトが生まれやすくもなり、多くの人が使うとなると、また別な課題も生じてきます。
開発時には、抽象化の先にある実際の機能やサービスの制限・特性をあまり気にしなくてもよい一方で、実際の運用時にはそれらの制限・特性について依然として理解している必要があります。
抽象化レイヤーで多くのことが隠蔽されればされるほど、開発は簡単になりますが、実際に使う際にどう運用すればよいのか分かりにくくなります。また、自分がやりたいことを実現するために、どういったライブラリやフレームワークを使えばよいのかも、分かりづらくなってきます。
Spring Bootでは、こういった課題を解決する手段を提供しています。例えば、自分が作りたいアプリケーションがWebシステムであり、データベースにはNoSQLであるMongoDBを使いたいといったことが決まれば、そのアプリケーションを作成する際の、依存するライブラリやデフォルトの設定などが自動的に行われます。
つまり、Spring Bootは望む構成を作るためのウィザード的なシステムとも言えます(図4)。
Spring BootのStarterとは
Spring Bootでは、どのような目的でシステムを作るのかに応じて、必要な設定とライブラリなどの依存関係を解決するための情報が定義セットとして管理されています。
例えば、Web開発する場合には、Spring MVCを使った開発に必要なライブラリの取得やそのための自動設定、Tomcatの設定なども自動で行います。また、Tomcat自体も組み込まれます。そのため、Spring Bootを使えば面倒な環境設定などを行わずにすぐに開発ができます。
依存関係だけであれば、これまでもMavenやGradleを使えばできましたが、それらのライブラリやフレームワークを使う上での設定は自分で行う必要がありました。
Spring Bootでは、依存関係の解決にMavenやGradleなどのツールを使うことは同様ですが、設定は自動で行われます。この機能はAutoConfigureと呼ばれています。そして、こうした定義セットをStarterと呼んでいます。
Starterは目的に応じて組み合わせて使うことができるので、自分のプロジェクトに応じた形にカスタマイズして利用することもできます。
公式で多くのStarterが用意されていますが、必要なセットがない場合にはStarterは自分でも作成することができます。ここですべてのStarterを紹介することは難しいですが、利用する方が多いと思われるものをいくつか紹介します。
spring-boot-starter
最も基本的なStarterで、他のStarterの元となっています。Spring Bootを最小構成などで使いたい場合に利用するStarterです。
spring-boot-starter-web
Web関連での最も基本的なStarterです。Web関連で開発を始めたい場合には、通常はこのStarterで問題ありません。Spring MVCを使ったアプリケーションが作成可能です。デフォルトでTomcatも含まれているので、すぐにWeb開発を始められます。
Tomcatを使いたくない場合でもこのStarterを使うことができます。ただし、その場合には、内部で依存しているspring-boot-starter-tomcatの代わりに他のスターターを有効にする必要があります。例えば、Tomcatの代わりにJettyをWebアプリケーションサーバとして利用する場合には、spring-boot-starter-jettyを併せて利用します。
spring-boot-starter-webflux
通常のブロッキングIOやスレッドモデルでのWebアプリケーションではなく、ノンブロッキングIOや少ないスレッドで、大量の接続などに対応しなければいけないWebサービスを作成するためのStarterです。
Spring MVCの代わりに、Spring WebFluxを使います。デフォルトで内部ではNettyを使っているので、こういったノンブロッキングIOをサポートするネットワーク環境が必要な場合には利用を検討するとよいでしょう。
spring-boot-starter-actuator
アプリケーションの状態のモニターやヘルスチェックなどの機能を追加します。一般的には、他のStarterと共に使われ、spring-boot-starter-webなどと一緒に利用されることを想定しています。
spring-boot-starter-batch
バッチ処理のためのSpringのプロジェクトであるSpring Batchを使ったアプリケーションを作成するためのStarterです。
spring-boot-starter-jdbc
SpringのプロジェクトであるSpring Data (JDBC Extensions)を使う場合のStarterです。JDBCを使ったデータベース処理や接続プールなどその他の管理などを行うことができます。
spring-boot-starter-integration
EIP(Enterprise Integration Patterns)フレームワークであるSpringのプロジェクト、Spring Integrationを使うためのStarterです。同様のEIPフレームワークとしてApache Camelなども有名ですが、Apache Camelからはcamel-spring-boot-starterというStarterが提供されています。
spring-boot-starter-test
Spring Bootを使ったアプリケーションのテストをするためのStarterです。JUnitやMockitoなどに依存していています。
ここで紹介したStarter以外にも、GitHubを見ると非常にたくさんのStarterがあることが分かります。
多くの方がspring-boot-starter-webを使っているため、Spring BootがWebアプリケーションを作成するためのフレームワークと認識している方もいるようですが、必ずしもWeb開発に限定されたものではありません。
また、これらのStarterの組み込み方は次回説明しますが、これらのコードを読む必要はないので心配はいりません。Spring Boot用のプロジェクトを構築するためのツールが提供されているので、それらを使えば簡単に構成をカスタマイズすることができます。
ただし、ツールだけではできない場合や、より高度なカスタマイズをしようと思ったときにGitHub内のコードを参照することで解決しやすくなるので、こういった情報があることを知っておくのは大切です。
Spring Bootとは(2)
アプリケーションの実行方法から配布形態までをカバーするフレームワーク
Spring Bootが他のフレームワークと大きく異なるもう1つの特徴として、アプリケーションの実行方法から配布形態までカバーしている点が挙げられます。
Spring Bootでプロジェクトを作ると、リスト2の通りメインクラスが用意されます。Webフレームワークなどではメインクラスを自分で触ることができないことが多々ありますが、さまざまな利用ケースを想定した場合にメインクラスを自分で変更可能なことは非常に大切です。
特にミドルウェアなどを組込み用として内包してしまう場合には、その起動から停止までのライフタイムを完全にコントロールしたいことがあります。その場合、メインクラスから提供されているので、それらのライフタイムは完全にコントロール可能です。
また、メインクラスが提供されていることで、Java初心者にとってもイメージしやすい構造になっていると思います。
package com.coltware.springboot.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Part1Application { public static void main(String[] args) { SpringApplication.run(Part1Application.class,args); } }
そして、Spring Bootをビルドすると実行可能な1つのJarファイルができあがり、その中に依存しているライブラリなどがすべて含まれるようになっています。従って、できあがったJarファイルを実行する際にもリスト3の通り1つのファイルを指定するだけです。
$./gradlew build // ・・省略・・・ $java -jar build/libs/demo-0.0.1.jar . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.0.RELEASE) 2018-11-25 15:51:52,883 INFO [main] boot.StartupInfoLogger (StartupInfoLogger.java:50) - Starting Part1Application on xxxxxx with PID 25343 2018-11-25 15:51:52,890 DEBUG [main] boot.StartupInfoLogger (StartupInfoLogger.java:53) - Running with Spring Boot v2.1.0.RELEASE, Spring v5.1.2.RELEASE 2018-11-25 15:51:52,891 INFO [main] boot.SpringApplication (SpringApplication.java:675) - No active profile set, falling back to default profiles: default 2018-11-25 15:51:55,049 INFO [main] boot.StartupInfoLogger (StartupInfoLogger.java:59) - Started Part1Application in 2.544 seconds (JVM running for 3.592)
1つのJarファイルで完結しているので、アプリケーションを配布する際にも1つのファイルで済みます。また、実行時に依存するライブラリのバージョンなどが環境によって異なることもなくなります。
こうした特徴は単純ではありますが、Dockerなどのコンテナ上で動的にサービスを起動したり落としたりする場合のメンテナンスが非常に簡単になります。また、依存しているライブラリはJarファイル内のBOOT-INFフォルダに含まれるので、ライブラリとバージョンなどはそこから調べることもできます。
Java 11への対応状況
2018年10月30日にリリースされたSpring Boot 2.1からJava 11がサポートされました。内部で利用しているSpring FrameworkのバージョンもJava 11対応された5.1に変更されています。また、まだまだJava全体としてJava 8がベースとして普及していることもあり、Java 8での互換性に関しても問題なくサポートされます。
もちろん、Java 9以降モジュール対応されたアプリケーションにも対応しています。しかし、Spring Bootで作成するアプリケーションが、依存するすべてのライブラリも含めて、モジュール対応されているわけではありません。
このため、モジュール対応されたアプリケーションを作る場合にはSpring Bootとは関係がない課題や疑問が生じると思われます。Spring Bootを初めて使う場合には避けた方がよいと思います。
最後に
Springのプロジェクトも非常に多岐にわたるようになり、どのプロジェクトがどのように使われているのか、どのように使えばよいのか分かりにくくなってしまっています。筆者にとっても、久しぶりにSpringプロジェクトのWebサイトを見ると、どこから手をつけてよいのか迷うほどプロジェクトが増えています。
また、Springプロジェクトに限らず、さまざまなフレームワーク、ライブラリも含めるとますますどのような構成がよいのか分かりにくいでしょう。
Spring Bootはこういった状況の中で、組み合わせの成功パターンを管理するためのフレームワークと言えます。従って、Spring Bootだけを見ていると全体像がつかみにくいのですが、Spring Frameworkの特徴やこれまでの課題点が分かると、Spring Bootがより理解しやすくなると思います。
そして、各種ライブラリやミドルウェア単体などのノウハウは十分蓄積されていると言えますが、組み合わせのノウハウはなかなか見つかりません。Spring Bootを使うことで組み合わせが共通化されやすくなります。また、Starterというキーワードもあるので、迷った場合にも同様の課題を持った人などが既に共有しているケースもありノウハウも見つけやすいはずです。
こういった背景がSpring Bootの注目度が高まっている理由の1つではないかと思います。次回からは、サンプルアプリケーションを作りながらSpring Bootの使い方を説明していきます。
