http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle のPart Iをテキトーに訳した。
内容的にはイマサラだし、ぐぐると既に訳されている方いるから二番煎じ感あるし、序章なので技術的にはそんな大したこと書いてないんで、資料的価値は無いんだけど。目的はSpringと英語の勉強がだから気にしない。
Part I. Overview of Spring Framework
Spring Frameworkは、軽量で、エンタープライズアプリケーションを構築し始めるのにワンストップのソリューションとなりうる可能性を秘めています。Springはモジュール化されており、必要なパーツのみを選ぶことが可能です。IoCコンテナ上に任意のwebフレームワークを使用出来ますが、Hibernate integration codeもしくはJDBC abstraction layerだけしか使えません。Spring Frameworkは宣言的トランザクション管理をサポートしており、RMIやwebサービス経由でロジックにリモートアクセスしたり、データの永続化には様々なオプションを提供しています。フル機能(full-featured)のMVC frameworkではAOP透過性を統合可能です。
Springは非侵入型(non-intrusive)に設計されています。つまり、ドメインのロジックはフレームワーク自身には通常は依存しない、という意味です。インテグレーションレイヤー(データアクセスレイヤーなど)では、データアクセス技術とSpringライブラリへの依存性が存在します。しかし、そうした依存性はそれ以外のコードベースとの分離は容易であるべきです。
このドキュメントはSpring Frameworkの機能のリファレンスガイドです。リクエストやコメント、質問があれば、user mailing listに投稿してください。Framework自体に対する質問はStackOverflow(https://spring.io/questions)で行うようにしてください。
1. Getting Started with Spring
このリファレンスガイドではSpring Frameworkに関する詳細な情報を提供しています。全機能に関する包括的なドキュメントに加え、Springが採用した基本的なコンセプト("Dependency Injection"など)の背景についても含まれます。
Springで開発を始めようとしている場合、Spring Bootベースのアプリケーションを作成することからSpring Frameworkを使い始めるのが良いでしょう。Spring BootはSpringベースのアプリケーションをすばやく*1開発する方法を提供します。Spring BootはSpring Frameworkをベースに作られており、convention over configurationを採用し、可能な限り素早く開発者の手元で実行可能なように設計されています。
ベースとなるプロジェクトを生成するためにstart.spring.ioを使用したり、"Getting Started" guidesからGetting Started Building a RESTful Web Serviceなど適当なガイドを選んでそれに従う方法も可能です。このガイドは目的に特化(task focused)しており、読みやすいように要約されています。また、ガイドの多くはSpring Bootをベースに作られています。個々の問題を解決するときに考慮するSpringの他プロジェクトについても網羅しています*2。
2. Introduction to the Spring Framework
Spring Frameworkとは、Javaアプリケーション開発をサポートする包括的なインフラを提供する、Javaのプラットフォームです。Springはインフラを扱い、開発者がアプリケーション開発に集中できるようにします。
Springは"plain old Java objects" (POJOs)でアプリケーションを開発可能にし、エンタープライズのサービスがPOJOに侵入しないようにします。この機能はJava SEのプログラミングモデルと完全およびJava EEの一部に適用されます。
アプリケーション開発者は以下のような利点をSpringプラットフォームから得られます。
- トランザクションAPIを用いることなくデータベーストランザクション内でJavaのメソッド実行が可能。
- リモートAPIを用いることなく、ローカルのJavaメソッドをリモートプロシージャに出来る。
- JMX APIを用いることなく、ローカルのJavaメソッドを管理操作に出来る。
- JMS APIを用いることなく、ローカルのJavaメソッドをメッセージハンドラに出来る。
2.1 Dependency Injection and Inversion of Control
Javaアプリケーション――この曖昧な用語は広い領域で使われています。制約のあるn層の組み込みアプリケーションからサーバーサイドのエンタープライズアプリケーションまで――これらのアプリケーションは、適切なアプリケーションを構築するのにオブジェクトの相互作用から成るのが一般的です。アプリケーションにおけるオブジェクトは相互に依存性(dependencies)を持ちます。
Javaプラットフォームはアプリケーション開発機能の基礎を提供しますが、首尾一貫したルールの基に各ブロックを組織化する基本的な仕組みを欠いているため、その責任はアーキテクトと開発者のものとなっています。アプリケーションの様々なオブジェクトインスタンスとクラスをまとめるには、デザインパターンのFactory, Abstract Factory, Builder, Decorator, Service Locatorなどを使用すれば良いのですが、これらのパターンはただ単にベストプラクティスの名称・パターンの解説・適用箇所・そのパターンが扱う問題、などなどに過ぎません。パターンとは、アプリケーションで自分自身で実装しなければならないベストプラクティスを形式化したものです。
Spring Framework Inversion of Control (IoC) コンポーネントは、性質の異なるコンポーネントを組み合わせて動作可能なアプリケーションを構築する方法を提供することで、上記の問題に対処します。Spring Frameworkは、形式化されたデザインパターンを、アプリケーションに統合可能なファーストクラスオブジェクトとして、明示的に組み込みます。多くの組織やグループがSpring Frameworkを堅牢でメンテナンス性の高いアプリケーションを設計するために使用しています。
- Background
"質問があります。制御のアスペクトが反転する、とは何でしょうか?" "The question is, what aspect of control are [they] inverting?"*3 Martin Fowler氏は2004年に彼のサイト上で Inversion of Control (IoC)に関する質問を投稿しています。同氏はIoCをより自己説明的な名称に変更する提案を行い、Dependency Injectionを考え出しました。
2.2 Modules
Spring Frameworkは約20モジュールに分類される機能群で構成されています。モジュールのグループ分けは以下の図に示すように、Core Container, Data Access/Integration, Web, AOP (Aspect Oriented Programming), Instrumentation, Messaging, Test、になります。
以降のセクションでは各機能で利用可能なモジュールをアーティファクト名とそれがカバーする領域と共に列挙していきます。アーティファクト名はDependency Management toolsで使用されるartifact IDsと同等です。
2.2.1 Core Container
Core Containerはspring-core
, spring-beans
, spring-context
, spring-context-support
, spring-expression
(Spring Expression Language)モジュールで構成されています。
spring-core
とspring-beans
モジュールはSpring Frameworkの基礎となるパーツを提供しており、これにIoCとDependency Injection機能が含まれています。BeanFactory
はfactoryパターンの洗練された実装です。
BeanFactory
は自前のsingletonを作らなくても済むようにし、ロジックから依存性の仕様(specification of dependencies)*4と設定を切り離せるようにします。
Context(spring-context
)モジュールはCore and Beansモジュールをベースに作られており*5、フレームワークが指示する方法でオブジェクトにアクセスする手段を提供します。この関係はJNDIレジストリに似ています。ContextモジュールはBeansモジュールから機能を継承しており、また、国際化のサポート(ex. resource bundlesの使用など)・イベント伝播(event propagation)・リソース読み込み(resource loading)・Servletコンテナ等によるコンテキストの透過的な作成、を追加しています。また、ContextモジュールはJava EEの機能であるEJB, JMX, 基本的なリモート実行(basic remoting)などもサポートします。ApplicationContext
インタフェースはContextモジュールの重要な要素です。spring-context-support
は、一般的なサードパーティライブラリである、キャッシュ(EhCache, Guava, JCache)・メール(JavaMail)・スケジューリング(CommonJ, Quartz)・テンプレートエンジン(FreeMarker, JasperReports, Velocity)を、Springアプリケーションのコンテキストに統合するためのサポートを提供します。
spring-expression
モジュールは実行時のオブジェクトグラフの問い合わせと操作用に強力なExpression Languageを提供します。これはJSP 2.1仕様が定義するunified expression language (unified EL)の拡張です。この言語は、プロパティ値のsetとget(setting and getting property values)・プロパティ割当(property assignment)・メソッド呼び出し・配列要素アクセス・コレクションとインデクサ・論理演算子と算術演算子・ネームド変数(named variables)・SpringのIoCコンテナ経由で名前によるオブジェクト参照(retrieval of objects by name)、をサポートします。また、一般的なリスト集約と同様に射影と選択もサポートします。
2.2.2 AOP and Instrumentation
spring-aop
モジュールはAOPアライアンス準拠のアスペクト指向プログラミングの実装を提供します。このモジュールを使用することで、分離すべき機能のコードを明確に分離するためにメソッドインターセプターとポイントカットを定義可能です。ソースレベルメタデータ機能を使用することで、コードに振る舞いに関する情報を組み込めるようになります。これは.NET attributesと似たようなやり方です。
上記とは別のspring-aspects
モジュールはAspectJとの統合機能を提供します。
spring-instrument
モジュールはアプリケーションサーバで使用するclass instrumentationサポートとクラスローダ実装を提供します。spring-instrument-tomcat
モジュールはTomcat用のSpringのinstrumentationです。
2.2.3 Messaging
Spring Framework 4にはspring-messaging
モジュールが含まれており、これは、Message
,MessageChannel
, MessageHandler
,メッセージングベースアプリケーションの土台となるオブジェクト、などのSpring Integrationプロジェクトの中心となる抽象化*6を基に作られています。また、このモジュールにはメッセージをメソッドにマッピングするアノテーションも含まれます。このアノテーションはSpring のMVCアノテーションベースのプログラミングモデルに似ています。
2.2.4 Data Access/Integration
Data Access/IntegrationレイヤーにはJDBC, ORM, OXM, JMS, Transactionモジュールがあります。
spring-jdbc
モジュールは冗長なJDBCコーディングとデータベースベンダー固有のエラーコードの解析を無くすJDBC抽象化レイヤーを提供します。
spring-tx
モジュールはすべての任意のPOJOs (Plain Old Java Objects)と特別なインタフェースを実装するクラスでの記述的もしくは宣言的なトランザクション管理をサポートします。
spring-orm
モジュールは、JPA, JDO, Hibernateなどの、一般的なO/RマッピングAPIの統合レイヤーを提供します。spring-orm
モジュールを使用すると、Springが提供する他の全機能とO/Rマッピングフレームワークを組み合わせられます。たとえば、前述のシンプルな宣言的トランザクション管理機能などと組み合わせ可能です。
spring-oxm
モジュールは、JAXB, Castor, XMLBeans, JiBX, XStreamなどの、Object/XMLマッピング実装をサポートする抽象化レイヤーを提供します。
spring-jms
モジュール(Java Messaging Service)はメッセージのproducingとconsumingの機能を持ちます。Spring Framework 4.1以降では、spring-messaging
モジュールとの統合化を提供します。
2.2.5 Web
Web
レイヤーにはspring-web
, spring-webmvc
, spring-websocket
, spring-webmvc-portlet
モジュールがあります。
spring-web
モジュールは基本的なweb指向の統合化機能を提供します。たとえば、マルチパートファイルアップロード機能・Servletリスナーを使用するIoCコンテナの初期化・web指向アプリケーションコンテキスト、などです。また、HTTPクライアントとSpringのリモート実行サポートの一部も含みます。
spring-webmvc
モジュール(Web-Servletモジュールとも呼ばれる)にはSpringのmodel-view-controller(MVC)とwebアプリケーション用のREST Web Services実装があります。Spring MVCフレームワークは、ドメインモデルのコードとwebフォームとの明確な分離およびSpring Frameworkの他の機能との統合、を提供します。
spring-webmvc-portlet
モジュール(Web-Portletモジュールとも呼ばれる)はPortlet環境で使われるMVC実装を提供し、これはspring-webmvc
モジュールの機能と似ています。
2.2.6 Test
spring-test
モジュールはJUnitもしくはTestNGを使用するSpringコンポーネントのユニットテストと統合テストをサポートします。Spring ApplicationContext
の本番と同等なローディングとそうしたコンテキストのキャッシュを提供します。また、コードを独立してテストするためのモックオブジェクトも提供します。
2.3 Usage scenarios
前述したそれぞれの構成要素によって、多様な状況下でSpringが優れた選択となります。多様な状況とは、リソース制約のあるデバイスで動作する組み込みアプリケーションから、Springのトランザクション管理機能およびwebフレームワークインテグレーションを使用する本格的なエンタープライズアプリケーションまで、のことです。
Figure 2.2. Typical full-fledged Spring web application
Springの宣言的トランザクション管理機能は、たとえばEJBコンテナ管理トランザクションを使うように、webアプリケーションを完全にトランザクショナルにします。すべてのビジネスロジックはシンプルなPOJOで実装可能で、そのPOJOはSpringのIoCコンテナが管理します。追加可能なサービスとしては、電子メール送信・webレイヤから独立したバリデーション、があります。バリデーションルールは実行する場所を選べます。SpringのORMサポートはJPA・Hibernate・JDOと統合可能で、たとえばHibernateを使用する場合、既存のマッピングファイルとHibernateのSessionFactory
設定をそのまま使えます。Form Controllersはシームレスにwebレイヤとドメインモデルを統合するもので、ActionForms
もしくはHTTPパラメータをドメインモデルに変換するクラスを排除します。
Figure 2.3. Spring middle-tier using a third-party web framework
場合によっては、完全に別のフレームワークへと乗り換えられないこともあります。Spring Frameworkはそれ自体だけで完結する必要は無く、つまり、all-or-nothingなソリューションではありません。既存のStruts, Tapestry, JSFその他のUIフレームワークで作られたフロントエンドはSpringベースのミドル層と統合可能で、これによりSpringのトランザクション機能が使用可能になります。そのためには、単にApplicationContext
を使用してビジネスロジックを繋ぎ、webレイヤーを統合するにはWebApplicationContext
を使用します。
Figure 2.4. Remoting usage scenario
webサービス経由で既存のコードにアクセスする必要がある場合、SpringのHessian-
, Burlap-
, Rmi-``````JaxRpcProxyFactory
クラスが使えます。既存アプリケーションをリモートアクセス可能にするのはそれほど難しくありません。
Figure 2.5. EJBs - Wrapping existing POJOs
Spring FrameworkはEJB用のアクセスと抽象化レイヤを提供し、スケーラブル化や宣言的セキュリティを必要とするフェイルセーフなwebアプリケーションのために、ステートレスセッションビーンでラップすることで既存のPOJOを再利用できます。
2.3.1 Dependency Management and Naming Conventions
依存性管理(Dependency management)とDI(dependency injection)は異なります。アプリケーションにSpringの適当な機能を(DIなどで)入れ込むには、必要となるすべてのライブラリ(jarファイル)をアセンブルして、実行時にクラスパスを通す(場合によってコンパイル時にも)必要があります。それらのインジェクトされる依存性は仮想コンポーネントではなく、ファイルシステム上に実体のあるリソースです(大抵の場合は)。依存性管理のプロセスには、リソースの探索・保存とクラスパスへの追加、を含みます。依存性は、直接(direct)(例えば、あるアプリケーションは実行時にSpringに依存する)、もしくは、間接(indirect)(例えば、あるアプリケーションはcommons-pool
に依存するcommons-dbcp
に依存する)の形態を取ります。間接的な依存は"推移的"("transitive")とも言われ、そうした依存性は識別と管理が困難です。
Springを使う場合、必要なSpringのモジュールを構成しているjarを取得する必要があります。これを容易に行えるようにするため、Springは可能な限り依存性を分離したモジュールの組み合わせとしてパッケージ化をしています。たとえば、もしwebアプリケーションを書くのでないなら、spring-webモジュールは不要です。このガイド内でSpringライブラリモジュールを示す際、短縮した簡略名であるspring-*
やspring-*.jar
を使用し、ここでの*
はモジュールの短縮名を表します(例えば、spring-core
, spring-webmvc
, spring-jms
)。実際のjarファイル名はモジュール名とバージョン番号から成ることが多いです(例えば、spring-core-4.1.5.RELEASE.jar)。
Spring Frameworkのそれぞれのリリースバージョンは以下の場所にアーティファクトをパブリッシュしています。
- Maven Central - Mavenが検索する際のデフォルトリポジトリで、使用するのに追加の設定は必要ありません。また、Springが依存する多くのライブラリもMaven Centralで利用可能で、Springコミュニティの大多数が依存性の管理にMavenを使用しています。ここでのjarの名称は
spring-*-<version>.jar
形式でMavenのgroupIdはorg.springframework
です。 - Spring用のpublicなMavenリポジトリ - final GAリリースに加えて、このリポジトリはdevelopment snapshotsとmilestonesを公開しています。jarファイル名はMaven Centralと同じ形式で、Maven Centralの他のライブラリとSpringのdevelopmentバージョンを使いたい場合に役立ちます。また、このリポジトリではダウンロードの面倒を省くためにすべてのSpringのjarをまとめたディストリビューションZIPファイルも公開しています。
まず最初に依存性の管理方法を選びます。通常は、Maven, Gradle, Ivyなど、自動的に行う方法を推奨しますが、jarを手動でダウンロードする方法も可能です。
Springのアーティファクトのリストは以下の通りです。各モジュールの詳細な説明はSection 2.2, “Modules”を参照してください。
Table 2.1. Spring Framework Artifacts
GroupId | ArtifactId | Description |
---|---|---|
org.springframework | spring-aop | プロキシベースAOPサポート |
org.springframework | spring-aspects | AspectJベースのアスペクト |
org.springframework | spring-beans | Groovyを含むBeansのサポート |
org.springframework | spring-context | スケジューリングとリモーティングの抽象化を含むランタイムのApplicationコンテキスト |
org.springframework | spring-context-support | 一般的なサードパーティライブラリをSpringのApplicationコンテキストに統合するためのクラスサポート |
org.springframework | spring-core | ほとんどのSpringモジュールが使用するCoreユーティリティ |
org.springframework | spring-expression | Spring Expression Language (SpEL) |
org.springframework | spring-instrument | JVMブートストラップ用のInstrumentationエージェント |
org.springframework | spring-instrument-tomcat | Tomcat用のInstrumentationエージェント |
org.springframework | spring-jdbc | DataSourceセットアップとJDBCアクセスサポートを含むJDBCサポートパッケージ |
org.springframework | spring-jms | JMSメッセージ送受信用のヘルパークラスを含むJMSサポートパッケージ |
org.springframework | spring-messaging | メッセージングアーキテクチャとプロトコルのサポート |
org.springframework | spring-orm | JPAとHibernateサポートを含むO/Rマッピング |
org.springframework | spring-oxm | Object/XMLマッピング |
org.springframework | spring-test | Springコンポーネントのユニットテストと統合テストのサポート |
org.springframework | spring-tx | DAOサポートとJCA統合を含むトランザクションのインフラ |
org.springframework | spring-web | クライアントとwebリモーティングを含むWebサポートパッケージ |
org.springframework | spring-webmvc | webアプリケーション用のREST Web Serviceとmodel-view-controllerの実装 |
org.springframework | spring-webmvc-portlet | Portlet環境で使うためのMVC実装 |
org.springframework | spring-websocket | STOMPサポートを含むWebSocketとSockJSの実装 |
Spring Dependencies and Depending on Spring
Springは多くのエンタープライズおよびその他の拡張ツールのサポートと統合を提供しますが、必須となる依存性を最小限に抑えます。よって、シンプルなSpringの使い方のために沢山のjarライブラリを手動で(自動でやるのと同等な)検索してダウンロードはやらない方が良いです。オーソドックスな依存性の注入では必須となる外部依存性は一つだけで、例えばロギングがそれに当たります(ロギングのオプションの詳細な説明は以下を参照してください)。
次に、Springに依存するアプリケーション設定に必要となる基本的なやり方の概要を示します。Maven, Gradle, Ivyの順に説明します。以下の説明において何か不明な点がある場合、それぞれの依存性管理システムのドキュメントや、サンプルコードを参照してください。Spring自身はビルド時の依存性管理にはGradleを使用し、サンプルの多くはGradleもしくはMavenです。
Maven Dependency Management
依存性の管理にMavenを使用する場合、明示的にロギングの依存性を記述する必要は特にありません。たとえば、アプリケーションコンテキストの生成とアプリケーション設定用に依存性の注入を使用するには、Mavenの依存性は以下のようになります。
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.1.5.RELEASE</version> <scope>runtime</scope> </dependency> </dependencies>
これだけです。Spring APIがコンパイル時に不要であればスコープをruntimeに宣言可能です。これは基本的な依存性の注入の例です。
上記の例はMaven Centralリポジトリを使用しています。Spring Mavenリポジトリ(milestonesもしくはdeveloperスナップショット)を使用するには、Maven設定のリポジトリに以下を追加します。
<repositories> <repository> <id>io.spring.repo.maven.release</id> <url>http://repo.spring.io/release/</url> <snapshots><enabled>false</enabled></snapshots> </repository> </repositories>
milestones用。
<repositories> <repository> <id>io.spring.repo.maven.milestone</id> <url>http://repo.spring.io/milestone/</url> <snapshots><enabled>false</enabled></snapshots> </repository> </repositories>
snapshots用。
<repositories> <repository> <id>io.spring.repo.maven.snapshot</id> <url>http://repo.spring.io/snapshot/</url> <snapshots><enabled>true</enabled></snapshots> </repository> </repositories>
Maven "Bill Of Materials" Dependency
Mavenでは誤ってSpringのJARの異なるバージョンを混ぜてしまうことが可能です。たとえば、サードパーティのライブラリやその他のSpringプロジェクトを見つけて、推移的な依存性が古いリリースを追加してしまう、などです。明示的に依存性を追加するのを忘れた場合、あらゆる予期しない問題が起こりえます。
そうしたトラブルを防ぐために、Mavenは "bill of materials" (BOM) という依存性の考え方をサポートします。dependencyManagement
セクションにspring-framework-bom
をインポートすることで、すべてのSpringの(直接的および推移的な)依存性が同一バージョンであることが保証されます。
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <version>4.1.5.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
BOMを使用するもう一つの利点はSpring Frameworkのアーティファクトの依存性に<version>
属性を指定する必要が無くなることです。
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <dependencies>
Gradle Dependency Management
GradleビルドシステムでSpringリポジトリを使用するにはrepositories
セクションに適切なURLを設定します。
repositories { mavenCentral() // and optionally... maven { url "http://repo.spring.io/release" } }
必要に応じてrepositories
のURLを/release
から/milestone
や/snapshot
に変更出来ます。リポジトリを設定した後はGradleの作法に沿って依存性が宣言出来ます。
dependencies { compile("org.springframework:spring-context:4.1.5.RELEASE") testCompile("org.springframework:spring-test:4.1.5.RELEASE") }
Ivy Dependency Management
Ivyで依存性の管理をしたい場合も似たような設定オプションを行います。
IvyがSpringリポジトリを参照するよう設定するには以下のようにivysettings.xml
にリゾルバを追加します。
<resolvers> <ibiblio name="io.spring.repo.maven.release" m2compatible="true" root="http://repo.spring.io/release/"/> </resolvers>
必要に応じてroot
URLを/release
から/milestone
や/snapshot
に変更出来ます。
設定をすると依存性が追加可能になります。例は(ivy.xml
など)以下の通りです。
<dependency org="org.springframework" name="spring-core" rev="4.1.5.RELEASE" conf="compile->runtime"/>
Distribution Zip Files
Spring Frameworkで推奨する方法は依存性の管理をサポートするビルドシステムを使うことですが、ディストリビューションZIPファイルをダウンロードすることも可能です。
ディストリビューションZIPはSpring Maven Repositoryにパブリッシュされています(これは我々自身の作業省力化のためであって、ダウンロードするだけならばMavenなどのビルドシステムは不要です)。
ディストリビューションZIPをダウンロードするにはwebブラウザで http://repo.spring.io/release/org/springframework/spring にアクセスして適切なバージョンのサブフォルダを選択します。
2.3.2 Logging
ロギングはSpringにとって非常に重要な依存性です。その理由は、a)唯一の必須な外部依存性、b)使用しているツールで出力を見たい人が多数、c)Springはロギングの依存性の選択肢すべてと多数のツールを統合する、ためです。アプリケーション開発者の理想の一つは、すべての外部コンポーネントを含むアプリケーション全体で、一か所に統合されたロギング設定をすることです。しかし、ロギングフレームワークに多数の選択肢が生まれてしまっている今日では、これは非常に困難です。
Springにおける必須のロギングの依存性はJakarta Commons Logging API (JCL)です。我々はJCLでコンパイルを行い、Spring Frameworkを拡張するクラスからはJCLのLog
オブジェクトが見えるようにしています。Springの全バージョンのユーザにとって重要なのは同じロギングライブラリを使用しているという点です。Springを拡張するアプリケーションにおいては後方互換性が保たれているのでマイグレーションは容易です。我々がそのためにしていることは、Springのモジュールの一つをcommons-logging
(JCLの標準実装)に明示的に依存させ、それ以外の全モジュールをコンパイル時にその一つのモジュールに依存させます。サンプルでMavenを使用している場合、commons-logging
の依存性がどこにあるのか不思議に思うでしょうが、具体的にはSpringのspring-core
という中心モジュールに含まれます。
commons-logging
の良いところはアプリケーションを動作させるのに特になにもしなくて良い点です。実行時探索アルゴリズム(runtime discovery algorithm)で動作し、クラスパス上の一般的な配置にあるその他のロギングフレームワークを検索して、適切と判断(もしくは事前に指定)したものを使用します。利用可能なものが無ければJDK(java.util.logging略してJUL)が適当に良い感じのログ出力をします。その場合、コンソールに適当なログが出力されてSpringアプリケーションが動作しているのが確認できるでしょう*7。
Not Using Commons Logging
不幸なことにcommons-logging
の実行時探索アルゴリズムはユーザにとって便利ですが一度トラブルになると解決が困難です。たとえば、時計の時間を戻してから新規プロジェクトとしてSpringを開始すると、異なるロギングの依存性が使われる場合があります。解決策の第一候補としてはSimple Logging Facade for Java (SLF4J)を使うことで、これはアプリケーション内部でSpringと共にユーザが使用するツールの多くでも使用しています。
commons-logging
を切り替えるには基本的には二種類の方法があります。
spring-core
モジュールから依存性を除外する(commons-logging
に明示的に依存しているのはこのモジュールだけ)。- 空のjarでライブラリを置換した特別な
commons-logging
への依存性を作る(詳細はSLF4J FAQ)を参照)。
commons-loggingを除外するにはdependencyManagement
セクションに以下を追加します。
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.5.RELEASE</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
この状態にすると、クラスパス上にJCL APIの実装が無くなるのでアプリケーションは壊れるので、替わりを用意する必要があります。次のセクションではサンプルとしてSLF4Jを使用するJCLの実装を替わりに提供する方法を説明します。
Using SLF4J
SLF4Jはcommons-logging
より効率的で依存性が明確です。その理由はロギングフレームワークを実行時に検索する代わりにコンパイル時にバインドするためです。つまり、実行時にどうしたいかを明確にしておき、作法に従って設定や宣言をする必要があります。SLF4Jは様々なロギングフレームワークとのバインディングを提供するので、通常は既に使用しているライブラリを選び、設定でそれとのバインディングを行います。
SLF4Jは様々なロギングフレームワークとのバインディングを提供しており、その中にはJCLも含まれており、逆もまた可能で、自分自身と他のロギングフレーム間とをブリッジします。SLF4JをSpringで使用するにはcommons-logging
依存性をSLF4J-JCLブリッジで置き換える必要があります。この設定を行うとSpring内のログ呼び出しはSLF4J APIのログ呼び出しに変換されます。そのため、アプリケーション内で他のライブラリがこのAPIを使用している場合、ログの管理と設定は一か所になります。
よくあるやり方としては、SpringからSLF4Jにブリッジし、更にSLF4JからLog4Jへバインディングを行います。そのためには4つの依存性を追加します(加えてcommons-logging
のexcludeも)。4つとは、ブリッジ・SLF4J API・Log4Jへのバインディング・Log4J実装、になります。Mavenでは以下のように設定します。
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.5.RELEASE</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.14</version> </dependency> </dependencies>
ロギングのためだけにやたらと多くの依存性を追加しているように見えます。とはいえ、これはオプションではありますが、クラスローダー問題に関してはデフォルトのcommons-logging
よりもまともな振る舞いをするでしょうし、OSGiプラットフォームのようなstrictなコンテナ上で開発している場合は特にそうです。また、未検証ですが、バインディングを実行時でなくコンパイル時に行うのでパフォーマンスが良いとも伝え聞いています。
SLF4Jユーザにとって更に一般的な選択肢は、より少ない手順とより少ない依存性で済む、Logbackと直接バインディングする方法です。LogbackはSLF4Jを実装しているので余分なバインディングが無くなり、ライブラリは4つではなく2つ(jcl-over-slf4j
とlogback
)に依存するだけになります。この場合、他の(Springを除く)依存性からslf4j-apiの依存性をexcludeする必要があるかもしれません。その理由は、クラスパス上にはそのAPIの一つのバージョンだけが存在するようにしたいからです。
Using Log4J
ロギングフレームワークのLog4jを使用しているユーザは多数存在します。効率的で枯れているため、Springのビルドとテストの実行時に使用しています。また、SpringはLog4jの初期化と設定用のユーティリティを提供しており、オプションでいくつかのモジュールのLog4jにコンパイル時の依存性を持たせます*8。
デフォルトのJCL依存性としてLog4jを動かすには、クラスパスにLog4jを追加して設定ファイル(クラスパスのルートにlog4j.properties
もしくはlog4j.xml
)を作成します。Mavenを使用している場合は以下のように依存性を宣言します。
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.5.RELEASE</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.14</version> </dependency> </dependencies>
以下はlog4j.propertiesをコンソールにログ出力する場合にサンプルです。
log4j.rootCategory=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n log4j.category.org.springframework.beans.factory=DEBUG
Runtime Containers with Native JCL
コンテナ自身が提供するJCLの実装でSpringアプリケーションを動作させているユーザは数多く存在します。IBM Websphere Application Server (WAS)はarchetypeです。これはしばしば問題を引き起こし、不幸なことに銀の弾丸は存在しません。アプリケーションからcommons-logging
を単に除くだけでは大抵のケースで不十分です。
この点についてもう少し詳しく説明します。報告される問題の多くはJCLそれ自身ではなくcommons-logging
にあり、ユーザはcommons-logging
から別のフレームワーク(たいていLog4J)へバインディングしています。これは失敗することがあります。その理由は、commons-logging
は、コンテナ上の古いバージョン(1.0)とユーザが使用する新しいバージョン(1.1)とで、ランタイム探索の方法を変更しているためです。SpringはJCL APIの独自部分(any unusual parts)を使用してないため、ここでは問題は起きませんが、Springやアプリケーションがログ出力を試みると、Log4Jとのバインディングが動作しないのを目の当たりにします。
WASのケースではクラスローダー階層(IBMでは"parent last"と呼称)を、コンテナではなくアプリケーションがJCLの依存性を制御するように、差し替えるのが最も簡単です。このオプションは何時でも可能ですが、別のアプローチがインターネット上に多数寄せられており、実際のバージョンとコンテナの機能セットに応じて対応を変える必要があるでしょう。
*1:原文は「quick (and opinionated)way」となっているが、何がどうopinionatedなのか分からなかったので訳していない
*2:原文は「They also cover other projects from the Spring portfolio」cover A from Bがどーもうまく訳せず、変な日本語になった
*3:こういう素朴な文はなかなかうまく訳せない……
*4:日本語の対訳が何のことか分からず
*5:原文は builds on the solid base provided byで、solid baseを訳に盛り込めてないけど、まぁ意味はあんま変わらなそうだから無視した
*6:key abstractionsが原文。対訳が分からない…
*7:You should find that your Spring application works and logs happily to the console out of the box in most situations, and that’s important.が原文。あんまうまく訳せてる自信がない
*8:so it has an optional compile-time dependency on Log4j in some modules.が原文。直訳したけどこれで合ってるのだろうか?