http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ のPart IIIをテキトーに訳した
このあたりからbean
を訳すのに、そのまま「bean」とするか「ビーン」とカタカナにするかで迷いだし、ゴッチャになっている。通読すると違和感大だろうけど、個人の日記レベルのテキトーな訳なので、カンベンして頂きたい。
Part III. Core Technologies
リファレンスドキュメントの本パートではSpring Frameworkに不可欠な全テクノロジを網羅します。
その中で最も重要なのはSpring FrameworkのInversion of Control (IoC)コンテナです。Spring FrameworkのInversion of Control (IoC)コンテナの扱いの次にSpringのAspect-Oriented Programming (AOP)が続きます。Spring Frameworkは自前のAOPフレームワークを持ち、理解が容易で、JavaエンタープライズプログラミングにおいてAOPに要求されるスイートスポットの80%をカバーします。
SpringインテグレーションによってAspectJ(機能面では現在最も豊富で、Javaエンタープライズの領域のAOP実装では確実に最も成熟している)も使用できます
Springチームはソフトウェア開発にテスト駆動開発(TDD)アプローチを採用することを推奨しており、Springは統合テストをサポートをしています(ユニットテストのベストプラクティスも)。SpringチームはIoCを正しく使うことでユニットテストと統合テストが容易になることを発見しました(クラスにsetterメソッドと適切なコンストラクタが存在すればservice locator registriesなどをセットアップしなくてもテスト時にインスタンス同士を結び付けるのは容易です)。テストに関する章を読めばおそらく納得して頂けるかと思います。
- Chapter 5, The IoC container
- Chapter 6, Resources
- Chapter 7, Validation, Data Binding, and Type
- Chapter 8, Spring Expression Language (SpEL)
- Chapter 9, Aspect Oriented Programming with Spring
- Chapter 10, Spring AOP APIs
- Chapter 11, Testing
5. The IoC container
5.1 Introduction to the Spring IoC container and beans
この章ではInversion of Control (IoC)のSpring Frameworkでの実装について解説します。なお、IoCはdependency injection (DI)とも呼びます。IoCは依存オブジェクトの定義を処理します。依存オブジェクトとは自身が共に動作する他のオブジェクトのことで、ファクトリメソッドが生成したり戻り値として返すオブジェクトインスタンスを、プロパティ・ファクトリメソッドへの引数・コンストラクタ引数、などを通じて設定します。コンテナは生成したbeanをそれらの依存オブジェクトにinjectsします。これは、Service Locatorパターンなどのメカニズムやクラスをコンストラクタで直接生成して依存オブジェクトの初期化や指定の制御をbean自身が行うのに比べると、基本的には処理順序が反転しており、よって、Inversion of Control (IoC)(制御の反転)と呼ばれています。
org.springframework.beans
とorg.springframework.context
パッケージがSpring FrameworkのIoCコンテナの基礎部分です。BeanFactory
インタフェースは任意の型のオブジェクトを管理する機能を持つ拡張設定を提供します。ApplicationContext
はBeanFactory
のサブインタフェースです。このサブインタフェースはSpring AOP機能を使用して連携を容易にするもので、メッセージリソースハンドリング(国際化に使用)・イベント発行・webアプリケーションで使用するWebApplicationContext
などのアプリケーションレイヤー固有のコンテキスト、に使用します。
簡潔に言えば、BeanFactory
は基本機能と設定フレームワークを提供し、ApplicationContext
はエンタープライズ固有の機能を追加したものです。ApplicationContext
はBeanFactory
の完全な上位集合(complete superset)で、この章ではSpringのIoCコンテナを説明するために使用します。ApplicationContext
ではなくBeanFactory
に関する情報についてはSection 5.16, “The BeanFactory”を参照してください。
Springでは、オブジェクトとはアプリケーションの骨格を形成するものであり、Spring IoC containerがオブジェクトを管理します。そうしたオブジェクトはbeansと呼びます。beanとはオブジェクトであり、Spring IoCコンテナがインスタンス化・アセンブル・管理を行います。別の面から見ると、beanはアプリケーションにおける多数のオブジェクトの内の単なる一つ、に過ぎません。Beansと、Beans間の依存関係は、コンテナが使用するconfiguration metadataを反映したものになります。
5.2 Container overview
インタフェースorg.springframework.context.ApplicationContext
はSpringのIoCコンテナを表現するもので、前述したbeanの初期化・設定・アセンブルの役割を担います。コンテナは設定メタデータを読み込み、初期化・設定・アセンブリ対象オブジェクトの命令を取得します。設定メタデータは、XML・Javaアノテーション・Javaコードで表現します。アプリケーションを構成するオブジェクトと、そのオブジェクト間の内部的な依存関係を表現可能です。
ApplicationContext
インタフェースのいくつかの実装は追加設定を必要としません*1。スタンドアローンアプリケーションではClassPathXmlApplicationContext
もしくはFileSystemXmlApplicationContext
を使うのが一般的です。設定メタデータの定義には伝統的なフォーマットとしてはXMLが使われていますが、メタデータフォーマットとしてJavaアノテーションやコードでコンテナに指示を与えることも可能です。Javaアノテーションやコードはメタデータフォーマットに宣言的サポートを可能にすることでXML設定を少なくします*2。
アプリケーションのシナリオの多くにおいて、明示的なユーザコードでSpring IoCコンテナの一つ以上のインスタンスをインスタンス化する必要はありません。たとえば、webアプリケーションのシナリオにおいて、web.xml
には8行(かもう少し)のボイラープレートなwebディスクリプタXMLで事足りることがほとんどです(Section 5.15.4, “Convenient ApplicationContext instantiation for web applications”を参照)。Spring Tool SuiteというEclipseベースの開発環境を使用する場合、このボイラープレートは数回のマウスクリックかキー操作で簡単に生成できます。
以下の図はSpringの動作を大雑把に示したものです。ApplicationContext
の生成と初期化後に、アプリケーションのクラスは設定メタデータと組み合わされ、システムもしくはアプリケーションで実行可能なすべて設定済みのインスタンスが得られます。
Figure 5.1. The Spring IoC container
5.2.1 Configuration metadata
上記の図に示すように、Spring IoCコンテナは設定メタデータ(configuration metadata)を使用します。この設定メタデータには、アプリケーション開発者がSpringコンテナにオブジェクトのインスタンス化・設定・アセンブルの指示を記述します。
設定メタデータは伝統的にシンプルで直観的なXMLで記述します。この章ではキーコンセプトとSpring IoCコンテナの機能を示すのに大抵はXMLを使用します。
設定メタデータの形式はXMLベースだけではありません。Spring IoCコンテナそれ自身は設定メタデータの実際の記述形式から完全に独立しています。最近ではほとんどの開発者がJava-based configurationを選択しています。
Springコンテナのメタデータの他の形式を使用する方法については、以下を参照してください。
- Annotation-based configuration: Spring 2.5でアノテーションベースの設定メタデータをサポートしました。
- Java-based configuration: Spring 3.0から開始された、Spring JavaConfigプロジェクトが提供する多数の機能をSpring Frameworkのコアの一部として取り込みました。これにより、XMLファイルではなくJavaコードを使用してアプリケーションクラス外にbeanを定義可能になりました。この機能を使用するには、
@Configuration
,@Bean
,@Import
,@DependsOn
を参照してください。
Springの設定は、コンテナ管理が必要な一つ以上のbean定義を、少なくとも一つ以上持ちます。XMLベースの設定メタデータではそうしたbeanの設定に、<bean/>
要素を含む<beans/>
トップレベル要素を使用します。Javaベースの設定では@Configuration
を付与したクラス内でメソッドに@Bean
アノテーションを付与します。
bean定義はアプリケーションを構成する実オブジェクトと対応関係にあります。一般的なケースではサービスレイヤーのオブジェクトを定義し、例えば、データアクセスオブジェクト(DAO)、StrutsのAction
などのプレゼンテーションオブジェクト、HibernateのSessionFactories
などのインフラオブジェクト、JMSQueues
、などです。一般的でないのは、コンテナには細粒度のドメインオブジェクトは設定しません。その理由は、ドメインオブジェクトの生成とロードの責務はDAOとビジネスロジックに負わせるのが自然なためです。しかし、IoCコンテナの制御下でない場所で生成したオブジェクトを設定するには、AspectJと共にSpring integrationを使用します。Using AspectJ to dependency-inject domain objects with Springを参照してください。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions go here --> </beans>
id
属性は文字列でbean定義を一意に識別するために使用します。class
属性はbeanの型を定義するもので完全修飾クラス名を使用します。id属性の値は協調オブジェクト*3を参照します。協調オブジェクトを参照するXMLは上記の例では示していません。詳細な情報についてはDependenciesを参照してください。
5.2.2 Instantiating a container
Spring IoCのインスタンス化は直線的です。ロケーションパスもしくはApplicationContext
のコンストラクタに渡されるパスが実際のリソースを表す文字列で、このリソースが文字列がコンテナに設定メタデータをロードするよう指示するものです。設定メタデータはJavaCLASSPATH
などを参照してローカルファイルシステムなどの外部リソースから参照します。
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
Spring IoCコンテナを学ぶと、SpringのResource
について知りたくなるかもしれませんが、それについてはChapter 6, Resourcesに説明があり、URI定義のロケーションからInputStreamを読み込みための簡易メカニズムを提供します。具体的には、Resource
のパスはSection 6.7, “Application contexts and Resource paths”.で解説するアプリケーションコンテキストの構築に使用します。
以下の例はサービスレイヤーオブジェクト(services.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- services --> <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl"> <property name="accountDao" ref="accountDao"/> <property name="itemDao" ref="itemDao"/> <!-- additional collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions for services go here --> </beans>
以下の例はデータアクセスオブジェクトdaos.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="accountDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao"> <!-- additional collaborators and configuration for this bean go here --> </bean> <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao"> <!-- additional collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions for data access objects go here --> </beans>
上記の例では、サービスレイヤは一つのPetStoreServiceImpl
クラスと二つのデータアクセスオブジェクト(JPAのO/Rマッピングベースの)JpaAccountDao
とJpaItemDao
で構成しています。property name
要素はJavaBeanのプロパティ名を参照し、ref
要素は別のbean定義を参照します。id
とref
要素間のリンクは協調オブジェクト間の依存性を表しています。オブジェクトの依存性の設定の詳細についてはDependenciesを参照してください。
Composing XML-based configuration metadata
bean定義を複数のXMLファイルに分割可能です。よくあるやり方としては、アーキテクチャの論理的なレイヤーやモジュールを表現する単位でXML設定ファイルを個々に分割します。
複数のXMLファイルからbean定義をロードするにはアプリケーションコンテキストのコンストラクタを使用します。前述の例のように、このコンストラクタは複数のResource
ロケーションを取ります。もしくは、別の一つ以上のファイルからbean定義をロードするには複数の<import/>
要素を使用します。
<beans> <import resource="services.xml"/> <import resource="resources/messageSource.xml"/> <import resource="/resources/themeSource.xml"/> <bean id="bean1" class="..."/> <bean id="bean2" class="..."/> </beans>
この例では、三つのファイルservices.xml
, messageSource.xml
, themeSource.xml
から外部bean定義をロードします。すべてのロケーションパスはインポートを行う定義ファイルの相対パスなので、services.xml
はインポート元ファイルと同一のディレクトリかクラスパスに配置する必要があり、messageSource.xml
とthemeSource.xml
はインポート元ファイルのディレクトリにあるresources
下に配置する必要があります。先頭のスラッシュは無視されますが、相対パスとして扱われるので、スラッシュは付けない方が良いです。インポートされるファイルの中身は、トップレベル要素に<beans/>
を含め、Spring Schemaに従うXML bean定義である必要があります。
可能ではあるものの、推奨されないこととして、相対パス"../"を使用して親ディレクトリのファイルを参照できます。これにより、あるアプリケーションの外側に位置するファイルで依存性を生成できます。特にこの参照方法はクラスパスURL(例えば"classpath:../services.xml")において非推奨です。実行時の解決処理が最も近い("nearest")クラスパスルートを選択し、親ディレクトリを参照します。クラスパスの設定が変更されると、以前とは異なる間違ったパスを選択する可能性があります。
相対パスではなく完全修飾リソースパスを使うことも可能です。たとえば、"file:C:/config/services.xml"や"classpath:/config/services.xml"などです。しかし、アプリケーションの設定が絶対指定と結合してしまう点に注意が必要です。そうした絶対指定を維持するのが好ましい場合は、たとえば、プレースホルダ―"${…}"を介して実行時にJVMシステムプロパティで与えます。
5.2.3 Using the container
ApplicationContext
はbeanとその依存性のレジストリを保持する拡張ファクトリの役割を持つインタフェースです。T getBean(String name, Class<T> requiredType)
メソッドを使用してbeanのインスタンスを取得します。
ApplicationContext
を使用して以下のようにbean定義の読み込みとアクセスを行います。
// create and configure beans ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); // retrieve configured instance PetStoreService service = context.getBean("petStore", PetStoreService.class); // use configured instance List<String> userList = service.getUsernameList();
beanのインスタンスの取得にはgetBean()
を使います。ApplicationContext
インタフェースは他にもbean取得用のメソッドがいくつかありますが、理想的にはアプリケーションコードでそれらは使わない方が良いです。実際には、アプリケーションコードはgetBean()
を呼ぶべきではなく、 Spring APIへの依存性も持つべきではありません。たとえば、Spring's integration with web frameworksはコントローラーとJSFマネージドbeanなど各種のwebフレームワーククラス用のDIを提供します。
5.3 Bean overview
Spring IoCコンテナは一つ以上のbeansを管理します。beanは設定から作られ、コンテナにXML<bean/>
定義の形式などで設定を与えます。
コンテナ内部ではbean定義はBeanDefinition
として保持しており、以下のメタデータを持ちます。
- パッケージ修飾クラス名(package-qualified class name): 通常はbean定義の実装クラス。
- Beanの振る舞い設定要素。beanがコンテナ上でどのような振る舞い(スコープ、ライフサイクル、コールバックなど)をすべきかの宣言。
- あるbeanを実行するに必要となる他のbeanへの参照。この参照は協調(collaborators)もしくは依存(dependencies)とも呼びます。
- 新規生成オブジェクトにセットするその他設定。たとえば、beanで使用するコネクション数を管理するコネクションプールや、プールのサイズ制限などです。
メタデータは各bean定義を構成するプロパティへと変換されます。
Table 5.1. The bean definition
Property | Explained in… |
---|---|
class | Section 5.3.2, “Instantiating beans |
name | Section 5.3.1, “Naming beans” |
scope | Section 5.5, “Bean scopes” |
constructor arguments | Section 5.4.1, “Dependency injection” |
properties | Section 5.4.1, “Dependency injection” |
autowiring mode | Section 5.4.5, “Autowiring collaborators” |
lazy-initialization mode | Section 5.4.4, “Lazy-initialized beans” |
initialization method | the section called “Initialization callbacks” |
destruction method | the section called “Destruction callbacks” |
また、特定のbeanを生成する方法の情報を持つbean定義に加え、ApplicationContext
の実装はユーザがコンテナ外で生成した既存オブジェクトを登録することも可能です。登録を行うにはBeanFactory実装のDefaultListableBeanFactory
を返すgetBeanFactory()
メソッドでApplicationContextのBeanFactoryを取得します。DefaultListableBeanFactory
のregisterSingleton(..)
とregisterBeanDefinition(..)
メソッドを使用して登録を行います。しかし、一般的なアプリケーションではメタデータbean定義のみで十分です。
5.3.1 Naming beans
すべてのbeanは一つ以上の識別子を持ちます。識別子はbeanをホストするコンテナ内で一意にする必要があります。beanは通常一つだけ識別子を持ちますが、一つ以上を持たせたい場合は別名が作れます。
XMLベース設定メタデータでは、bean識別子にはid
とname
属性の両方またはいずれか一方を指定します。id
属性にはただ一つだけのidを指定します。慣例としてid名は英数字(myBean, fooServiceなど)で、特殊文字も可能です。beanに別名を与えたい場合、name
属性を指定可能で、カンマ(,
)・セミコロン(;
)・空白で区切ります。歴史的経緯により、Spring 3.1以前のバージョンでは、id
属性はxsd:ID
型として定義されており、文字列のみの制限があります。3.1以降の現在では、xsd:string
型として定義されています。注意点として、id
の一意性はコンテナが強制するもので、XMLパーサーでは行いません。
beanのidやnameの指定は必須ではありません。明示的にidやnameを指定しない場合、コンテナがbeanの一意な名前を生成します。しかし、ref
要素やService Locatorスタイルのルックアップでbean名による参照をしたい場合、nameの指定は必須です。nameを指定する必要が無いケースとしてはinner beansとautowiring collaboratorsを使用する参照です。
Bean Naming Conventions
規約(convention)とは、bean命名時のインスタンスフィールド名の標準的なJavaの規約を使用することです。bean名は小文字で始めてそれ以降はキャメルケースです。例としては(引用符除く)'accountManager'
, 'accountService'
, 'userDao'
, 'loginController'
などです。
一貫性のあるbeanの命名は設定の理解を容易にし、Spring AOPを使用する場合、名前で参照したbeanにアドバイスを適用するのに役立ちます。
Aliasing a bean outside the bean definition
bean定義において、beanには一つ以上の名前を持たせられます。id
属性で最大一つ、name
属性に任意の数、を指定します。これらの名前は同一beanへの同等な別名となり、様々な場面で役に立ちます。たとえば、あるコンポーネントに固有のbean名を使用することで、アプリケーションの各コンポーネントが共通の依存性を参照可能です。
定義するbeanにすべてのエイリアスを指定することは必ずしも適切ではありません。そのbeanとは別に定義するbean用のエイリアスを作る方が望ましい場合があります。これは、各サブシステムでそれぞれ独自のオブジェクト定義を持ち、設定を分離する大規模システムでは良くあるケースです。この場合、XMLベース設定メタデータでは<alias/>
要素を使用します。
<alias name="fromName" alias="toName"/>
上記の例の場合、同一コンテナ上にfromName
というbeanがあり、エイリアス定義後はtoName
として参照可能です。
例として、サブシステムA用の設定メタデータをsubsystemA-dataSource
という名前を介してあるDataSourceを参照し、サブシステムB用の設定メタデータはsubsystemB-dataSource
という名前を介してDataSourceを参照したいとします。また、両サブシステムを使用するメインアプリケーションをコンポーズするときメインアプリケーションはmyApp-dataSource
という名前を介してDataSourceを参照します。この三つの名前が同一オブジェクトを参照するには、MyApp設定メタデータに以下のエイリアス定義を追加します。
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/> <alias name="subsystemA-dataSource" alias="myApp-dataSource" />
このとき、各コンポーネントとメインアプリケーションは名前を介してdataSourceを参照でき、その名前は一意であり他の定義と(適切に作成された名前空間において)衝突しないことを保証し、また、各エイリアスは同一beanを参照します。
Java-configuration
Java-configurationを使用する場合、@Bean
アノテーションでエイリアスを作成可能です。詳細はSection 5.12.3, “Using the @Bean annotation”を参照してください。
5.3.2 Instantiating beans
bean定義とは基本的には一つ以上のオブジェクトを生成するレシピのようなものです。コンテナは要求されたnamed beanのレシピを探して、実オブジェクトを生成(ないし取得)するためにbean定義でカプセル化した設定メタデータを使用します。
XMLベース設定メタデータを使用する場合、<bean/>
要素のclass
属性にインスタンス化するオブジェクトの型(かクラス)を指定します。class
属性は内部的にはBeanDefinition
インスタンスのClass
プロパティであり基本的には必須(例外についてはthe section called “Instantiation using an instance factory method”とSection 5.7, “Bean definition inheritance”)です。Class
プロパティは以下のいずれかの方法で使用します。
- 通常は、コンテナ自身がコンストラクタをリフレクションで呼び出すことで直接beanを生成したいので、生成するbeanクラスを指定します。これはおおむね
new
演算子を使用したJavaコードと同等です。 - あまり一般的でないケースとしては、コンテナがbeanを生成するのにクラスの
static
ファクトリーメソッドを呼び出したい場合、オブジェクト生成を行うstatic
ファクトリーメソッドを持つクラスを指定します。static
ファクトリーメソッドの戻り値型はそのクラスと同じでもまったく別のクラスであっても構いません。
Inner class names. もしstatic
内部クラスをbean定義に設定したい場合、内部クラスのbinary名を使用します。
たとえば、com.example
パッケージにFoo
クラスがあり、Foo
クラスはBar
というstatic
内部クラスを持つとした場合、bean定義の'class'
属性は以下になります。
com.example.Foo$Bar
外部クラス名と内部クラス名との区切りには$
文字を使う点に注意してください。
Instantiation with a constructor
コンストラクタ方式でbeanを生成する場合、すべての通常クラスがSpringと互換性があります。つまり、開発済みのクラスに独自方式のコードを入れたり特定インタフェースを実装したりなどは必要ありません。単にbeanクラスを指定するだけで十分です。ただし、ある特定のbeanを使用するIoCの型に依りますが、デフォルト(空の)コンストラクタを必要とする場合もあります。
Spring IoCコンテナはユーザが望むおおむね全てのクラスを管理します。純粋なJavaBeansの管理のみには限定されません。たいていのSpringユーザは、JavaBeansにデフォルト(引数無し)コンストラクタのみと、コンテナのプロパティを手本とした適切なgetterとsetter*4を定義したものを好みます。また、 コンテナにはnon-bean-styleのクラスも使用可能です。たとえば、JavaBean仕様に全く準じていないレガシーコネクションプールを使用したい場合でも、Springは他と同様に管理可能です。
XMLベース設定メタデータでは以下のようにbeanクラスを指定します。
<bean id="exampleBean" class="examples.ExampleBean"/> <bean name="anotherExample" class="examples.ExampleBeanTwo"/>
コンストラクタに引数を与える方法やオブジェクト生成後にプロパティへオブジェクトインスタンスを設定する方法の詳細については、Injecting Dependenciesを参照してください。
Instantiation with a static factory method
staticファクトリーメソッドで生成するbeanを定義する場合、class
属性にはstatic
ファクトリーメソッドを持つクラスを指定して、そのクラス自身が持つファクトりーメソッド名をfactory-method
属性に指定します。このメソッドを(後に述べるように引数有も可能)呼ぶとliveオブジェクトが戻り、その後はコンストラクタ経由で生成したかのように扱えます。そうしたbean定義の使用例としてはレガシーコードのstatic
ファクトリーを呼び出す場合です。
以下にファクトリーメソッド呼び出しによってbeanを生成するbean定義を示します。この定義は戻り値オブジェクトの型(クラス)を指定せず、ファクトリーメソッドを持つクラスのみ指定します。この例では、createInstance()
メソッドはstaticが必須です。
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
public class ClientService { private static ClientService clientService = new ClientService(); private ClientService() {} public static ClientService createInstance() { return clientService; } }
ファクトリーメソッドへの引数指定やファクトリが返したオブジェクトのプロパティを設定する方法の詳細については、Dependencies and configuration in detailを参照してください。
Instantiation using an instance factory method
static factory methodのインスタンス化と似ていますが、インスタンスファクトリーメソッドによるインスタンス化は、コンテナが新しいbeanを生成するのに定義済みbeanの非staticメソッドを呼び出します。この機能を使用するには、class
属性は空にして、factory-bean
属性にコンテナ内のオブジェクトを生成するインスタンスメソッドを持つbean名を指定します。factory-method
属性にはファクトリーメソッド名を指定します。
<!-- the factory bean, which contains a method called createInstance() --> <bean id="serviceLocator" class="examples.DefaultServiceLocator"> <!-- inject any dependencies required by this locator bean --> </bean> <!-- the bean to be created via the factory bean --> <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private DefaultServiceLocator() {} public ClientService createClientServiceInstance() { return clientService; } }
以下のように、一つのファクトリクラスに複数のファクトリーメソッドを持つことも可能です。
<bean id="serviceLocator" class="examples.DefaultServiceLocator"> <!-- inject any dependencies required by this locator bean --> </bean> <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/> <bean id="accountService" factory-bean="serviceLocator" factory-method="createAccountServiceInstance"/>
public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private static AccountService accountService = new AccountServiceImpl(); private DefaultServiceLocator() {} public ClientService createClientServiceInstance() { return clientService; } public AccountService createAccountServiceInstance() { return accountService; } }
このやり方ではDI経由でfactory bean自体の管理と設定が可能です。詳細はDependencies and configuration in detailを参照してください。
Springドキュメントでは、factory beanとはSpringコンテナが管理するbeanのことでインスタンスもしくはstaticファクトリーメソッド経由でオブジェクトを生成するものの事を指します。また、FactoryBean
(単語の頭が大文字な点に注意)とはSpring固有のFactoryBean
のことを指します。
5.4 Dependencies
一般的なエンタープライズアプリケーションをオブジェクト一つ(Springで言うところのbean)だけで構成することはありません。同様に、例えごく単純なアプリケーションであっても、エンドユーザがアプリケーションを一枚岩のように操作しているよう見せるのにもいくつかの協調オブジェクトを持つものです。次のセクションでは、個々の独立した多数のbean定義から、仕様を実現するためにオブジェクトが協調動作する完全なアプリケーションを構築する方法を説明します。
5.4.1 Dependency injection
Dependency injection (DI)とは、依存関係を定義するオブジェクトを処理することで、依存関係とは、別のオブジェクトと協調動作することで、別のオブジェクトを渡す経路としては、コンストラクタ引数からのみ・ファクトリーメソッドの引数・ファクトリーメソッドの戻り値やコンストラクタで生成した後オブジェクトインスタンスにsetするプロパティ、などがあります。コンテナはbean生成時にそうした依存関係をinjectsします。このプロセスの流れは、bean自身がクラスを直接生成したりService Locatorパターンで依存関係の解決とインスタンス化を制御することに比べると、制御が反転しているため、Inversion of Control (IoC)と呼ばれます。
オブジェクトが依存関係と一緒に提供されると、コードはDIにより整理され疎結合が促進されます。オブジェクト自身は依存関係を解決せず、依存関係のクラスや場所も知りません。また、依存関係がインタフェースや抽象クラスなのでユニットテスト時にスタブやモック実装を使用可能な場合、クラスのテストが容易になります。
DIには主要なやり方が二つあり、それぞれ、Constructor-based dependency injectionとSetter-based dependency injectionです。
Constructor-based dependency injection
コンストラクタベース(Constructor-based) DIとは、依存関係を表現する複数の引数を持つコンストラクタをコンテナが呼び出すこと、を指します。beanを生成するのに指定の引数でstatic
ファクトリーメソッドを呼ぶことに相当し、この解説ではコンストラクタとstatic
ファクトリーメソッドの両方とも扱います。下記の例はコンストラクタインジェクションでのみ依存性を注入可能なクラスを示しています。注意点としては、このクラスは特別なことは何も無い普通のクラスで、コンテナ固有のインターフェースや基底クラス、アノテーションに何も依存しないPOJOです。
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on a MovieFinder private MovieFinder movieFinder; // a constructor so that the Spring container can inject a MovieFinder public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually uses the injected MovieFinder is omitted... }
Constructor argument resolution
コンストラクタ引数の解決はその引数の型を使用します。bean定義のコンストラクタ引数が潜在的な曖昧さを含まない場合、bean定義で定義したコンストラクタ引数の順序が、beanインスタンス時に適切なコンストラクタに提供する引数の順序となります。以下のクラスを例に取ります。
package x.y; public class Foo { public Foo(Bar bar, Baz baz) { // ... } }
潜在的な曖昧さは無く、Bar
とBaz
クラスは互いに継承関係に無いと仮定します。このとき以下の設定は正しく動作し、<constructor-arg/>
要素に明示的に各コンストラクタ引数が指す型を指定する必要はありません。
<beans> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> </bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> </beans>
既知の型で別のbeanを参照するときマッチングが発生します*5(前述の例のように)。<value>true</value>
などのsimple type*6を使う場合、Springは値の型を解決出来ず、何らかの助け無しに型をマッチングできません。以下の例を考えます。
package examples; public class ExampleBean { // Number of years to calculate the Ultimate Answer private int years; // The Answer to Life, the Universe, and Everything private String ultimateAnswer; public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } }
前述のシナリオでは、type
属性でコンストラクタ引数の型を明示的に指定すれば、コンテナはsimple typeでタイプマッチング出来ます。たとえば、
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean>
コンストラクタ引数のインデックスを明示的に指定するにはindex
属性を使います。
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean>
また、複数のsimple valuesの曖昧さを解決するには、同一の型で二つの引数を持つコンストラクタの曖昧さを解決するインデックスを指定します。なお、indexは0オリジンです。
値の曖昧さにはコンストラクタパラメータ名を使うことも出来ます。
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </bean>
なお、上記を動作させるためにはデバッグフラグをenabledにしてコンパイルする必要があり、そうするとSpringはコンストラクタのパラメータ名を参照可能になります。デバッグフラグでコンパイル出来ない(もしくは、そうしたくない)場合、コンストラクタ引数を明示的に命名するJDKの@ConstructorPropertiesアノテーションを使います。その場合サンプルクラスを以下のようにします。
package examples; public class ExampleBean { // Fields omitted @ConstructorProperties({"years", "ultimateAnswer"}) public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } }
Setter-based dependency injection
セッターベース(Setter-based)DIとは、引数無しコンストラクタもしくは引数無しstatic
ファクトリーメソッドでbeanを生成したあと、コンテナがbeanのsetterメソッドを呼び出すこと、を指します。
以下の例は純粋なsetterインジェクションでしか依存性を注入出来ないクラスを示しています。こうしたクラスはJavaではありふれたものです。このPOJOはコンテナ固有のインタフェース・基底クラス・アノテーションなどへの依存性はありません。
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on the MovieFinder private MovieFinder movieFinder; // a setter method so that the Spring container can inject a MovieFinder public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually uses the injected MovieFinder is omitted... }
ApplicationContext
は管理しているbeanのコンストラクタおよびセッターベースDIをサポートします。また、コンストラクタ方式経由で依存性をインジェクションしたあとのセッターベースDIのサポートもします。ある形式から別の形式へとプロパティを変換するPropertyEditor
インスタンスと連携するBeanDefinition
形式で依存性を設定します。しかし、普通のSpringユーザはこれらのクラスを直接使用したりはせず(つまり自前のコードを書いたりしない)XMLbean
定義やアノテーション付与コンポーネント(@Component
, @Controller
アノテーションなどを付与したクラスのこと)や@Configuration
クラスの@Bean
メソッドを使います。これらの元となるクラスは内部的にBeanDefinition
インスタンスに変換されてSpring IoCコンテナインスタンスをロードするのに使用されます。
Constructor-based or setter-based DI?
コンストラクタとセッターDIは両方混ぜて使用可能なので、必須の依存性( mandatory dependenciesにはコンストラクタ、任意の依存性(optional dependencies)には設定メソッドやsetterメソッドを使うのは経験則的にも良いやり方です。注意点として、依存性が必須のプロパティを示すのにはsetterメソッドに@Requiredアノテーションを使用します。
Springチームはコンストラクタインジェクションを基本的には推奨し、その理由は、イミュータブルオブジェクト(immutable objects)としてアプリケーションコンポーネントを実装し、必須の依存性がnull
でないことを保証出来るためです。また、コンストラクタでインジェクションしたコンポーネントは完全に初期化済みの状態でクライアント(が呼び出す)コードに返されます。付け加えると、大量のコンストラクタ引数は不吉なにおいbad code smellであり、これはクラスが責務過剰の兆候を示しているので、責務の適切な分割を行うリファクタリングをすべきでしょう。
セッターインジェクションはクラス内で適当なデフォルト値を設定可能な任意の依存性にのみ主に使用すべきです。そうでないと、その依存性を使用するコードすべてにnot-nullチェックが必須となります。セッターインジェクションが有用な一つの例としては、再設定や後で再インジェクションに従ってクラスのオブジェクトをセッターメソッドが行う場合です。JMX MBeans経由の管理はセッターインジェクションを使わざるを得ません。
そのクラスに最も適したDIスタイルを使用してください。時としてソースが非公開のサードパーティクラスを使用する場合、選択は開発者に委ねられます。たとえば、サードパーティクラスが一切セッターメソッドを公開しない場合、コンストラクタインジェクションDIのみが使用可能かもしれません。
Dependency resolution process
コンテナは以下のようにして依存性の解決(dependency resolution)を試みます。
- 全beanを記述する設定メタデータを使用して
ApplicationContext
が生成および初期化されます。設定メタデータは、XML・Javaコード・アノテーションなどで指定します。 - 各beanごとに、もし依存性が通常のコンストラクタの代わりに、プロパティ形式・コンストラクタ引数・staticファクトリーメソッドの引数が使用可能であれば、依存性をそれらで表現します。そうした依存性は、beanを実際に生成した時点(when the bean is actually created)で、beanに与えます。
- 各プロパティもしくはコンストラクタ引数が、値の実際の定義もしくはコンテナの別beanへの参照、になります。
- 各プロパティもしくはコンストラクタ引数が、指定フォーマットから、プロパティもしくはコンストラクタ引数の実際の型へと変換された値、になります。デフォルトで、Springは文字列形式の値を組み込み型、例えば
int
,long
,String
,boolean
、に変換可能です。
Springはコンテナ生成時に各beanの設定を検証します。ただし、beanのプロパティはbeanが実際に生成されるまで(is actually created)設定されません。Beanの生成は、コンテナ生成時に、シングルトンスコープかつ事前インスタンス化(がデフォルト動作)で行われます。スコープ定義はSection 5.5, “Bean scopes”にあります。要求した場合にのみbeanを生成することもできます。beanの依存性とその依存性の依存性(の依存性の~以下略)を生成して代入していくので、beanの生成の様子は潜在的にグラフを描くことになります。よって、注意点としては、依存性間の解決のミスマッチは影響を受けるbeanの初回生成時になって初めて表面化する可能性があります。
Circular dependencies
コンストラクタインジェクションを主に使用する場合、解決不能な依存性の循環になる場合があります。
たとえば、クラスAがコンストラクタインジェクションでクラスBのインスタンスを要求し、クラスBがコンストラクタインジェクションでクラスAのインスタンスを要求する場合です。互いにインジェクションしあうようにクラスAとBのbeanを設定すると、Spring IoCコンテナは実行時に循環参照を検出し、BeanCurrentlyInCreationException
をスローします。
一つの解決策としては、対象クラスのソースコードをコンストラクタではなくセッターで設定するように変更することです。あるいは、コンストラクタインジェクションではなくセッターインジェクションのみ使います。つまり、非推奨ですが、セッターインジェクションで循環依存を設定可能です。
(循環依存が無い)通常ケースとは異なり、bean Aとbean Bの循環依存は、あるbeanが自分自身の完全な初期化前にもう他方へ注入されるよう強制するものです(古典的な卵が先かニワトリが先か問題)。
通常はSpringが正常動作すると信頼して構いません。存在しないbeanの参照や循環依存などの設定上の問題はコンテナのロード時に検出します。Springはbeanを実際に生成する時点まで可能な限り依存性の解決とプロパティの設定を遅らせます。つまり、Springコンテナが正しくロードされたとしても、依存性やオブジェクト生成に問題がある場合そのオブジェクトをリクエストする時点で例外をスローする、という意味です。たとえば、存在しないか不正なプロパティとなる場合はbeanは例外をスローします。潜在的に設定上の問題が後になってから表面化する理由は、ApplicationContext
の実装がデフォルトでシングルトンビーンを事前インスタンス化するためです。beanが実際に必要になる前に生成するメモリと時間のコストを払えば、ApplicationContext
生成時に設定上の問題を発見できます。このシングルトンビーンを事前インスタンス化ではなく遅延初期化するようにデフォルトの振る舞いをオーバーライドすることは可能です*7。
循環依存が無くて、一つ以上の協調beanを依存beanに注入する場合、設定済みの各協調beanが依存beanに注入されます。つまり、bean Aがbean Bに依存する場合、Spring IoCコンテナはbean Aのセッターメソッド呼び出し前にbean Bを完全に設定する、という意味です。つまり、(もし事前インスタンス化シングルトンで無ければ)beanはインスタンス化され、依存性が設定され、関連ライフサイクルメソッド(configured init methodとInitializingBean callback methodなど)が実行される、ということです。
Examples of dependency injection
以下はXMLベース設定メタデータでのセッターDIの使用例です。Spring XML設定ファイルの一部でいくつかのbean定義をしています。
<bean id="exampleBean" class="examples.ExampleBean"> <!-- setter injection using the nested ref element --> <property name="beanOne"> <ref bean="anotherExampleBean"/> </property> <!-- setter injection using the neater ref attribute --> <property name="beanTwo" ref="yetAnotherBean"/> <property name="integerProperty" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i) { this.i = i; } }
上記の例では、セッターはXMLファイルのプロパティ指定と対になるよう宣言しています。以下の例はコンストラクタDIの例です。
<bean id="exampleBean" class="examples.ExampleBean"> <!-- constructor injection using the nested ref element --> <constructor-arg> <ref bean="anotherExampleBean"/> </constructor-arg> <!-- constructor injection using the neater ref attribute --> <constructor-arg ref="yetAnotherBean"/> <constructor-arg type="int" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ExampleBean( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { this.beanOne = anotherBean; this.beanTwo = yetAnotherBean; this.i = i; } }
bean定義のコンストラクタ引数指定はExampleBean
のコンストラクタ引数を使用します。
先の例に少し手を入れて、コンストラクタの代わりに、オブジェクトのインスタンスを返すstatic
ファクトリーメソッドを呼び出すよう指定します。
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg ref="anotherExampleBean"/> <constructor-arg ref="yetAnotherBean"/> <constructor-arg value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean { // a private constructor private ExampleBean(...) { ... } // a static factory method; the arguments to this method can be // considered the dependencies of the bean that is returned, // regardless of how those arguments are actually used. public static ExampleBean createInstance ( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...); // some other operations... return eb; } }
static
ファクトリーメソッドの引数はコンストラクタの場合と同様に<constructor-arg/>
要素で指定します。ファクトリーメソッドが返すクラスの型はstatic
ファクトリーメソッドを持つクラスと同じ型であってはいけません、サンプルではそうなっていますが*8。インスタンス(非staticな)ファクトリーメソッドは同様な方法で使用可能(class
属性の代わりにfactory-bean
属性を使用する点が異なる)なので詳細についてはここでは解説しません。
5.4.2 Dependencies and configuration in detail
前述のセクションでは、beanプロパティ・別の管理ビーン(協調オブジェクト)への参照としてのコンストラクタ引数・インラインでの値定義、のやり方を解説しました。SpringのXML設定メタデータは<property/>
と<constructor-arg/>
要素内にサブ要素型をサポートします。
Straight values (primitives, Strings, and so on)
<property/>
要素のvalue
属性は人が読める形式の文字列表現としてコンストラクタ引数やプロパティに指定します。Springのconversion serviceがString
からプロパティや引数の実際の型への変更を行います。
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- results in a setDriverClassName(String) call --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="root"/> <property name="password" value="masterkaoli"/> </bean>
以下の例はXML設定を簡潔にするために同様な内容をp-namespaceを使用したものです。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/mydb" p:username="root" p:password="masterkaoli"/> </beans>
上記のXMLは簡潔になっていますが、タイプミスは作成時ではなく実行時に検出されます。ただし、bean定義作成時にオートコンプリートをサポートするIntelliJ IDEAやSpring Tool Suite (STS)などのIDEを使用している場合は、その限りではありません。
また、java.util.Properties
インスタンスも設定できます。
<bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <!-- typed as a java.util.Properties --> <property name="properties"> <value> jdbc.driver.className=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mydb </value> </property> </bean>
SpringコンテナはJavaBeansのPropertyEditor
を使用して<value/>
要素内のテキストをjava.util.Properties
インスタンスに変換します。これは簡潔な記法であり、Springチームがvalue
属性スタイルでのネストされた<value/>
要素の使用を好む数少ないケースの一つです。
The idref element
idref
要素はコンテナの別beanのid(文字列値で参照では無い)を<constructor-arg/>
や<property/>
に渡す際に記述エラーの可能性を少なくするのに使えます。
<bean id="theTargetBean" class="..."/> <bean id="theClientBean" class="..."> <property name="targetName"> <idref bean="theTargetBean" /> </property> </bean>
上記のbean定義は以下と(実行時には)正確に同等です。
<bean id="theTargetBean" class="..." /> <bean id="client" class="..."> <property name="targetName" value="theTargetBean" /> </bean>
前者の形式のほうが後者より好ましいです。その理由は、idref
タグによりコンテナがネームドビーンが実際に存在する参照かデプロイメント時に検証可能になるためです。後者の例では、client
beanのtargetName
プロパティに渡される値には検証は実行されません。タイプミスはclient
beanが実際にインスタンス化される時にのみ(大抵の場合は致命的エラーとして)発見されます。client
beanがprototype beanである場合、タイプミスによる例外のスローはコンテナデプロイ後かなり経ってからのみ表面化する可能性があります。
idref
要素のlocal
属性は4.0 beans xsdではサポートしません。その理由は、標準的なbean
参照を通して値を提供することはもう無いためです*9。4.0スキーマへのアップグレード時には単に既存のidref local
をidref bean
に変更します。
<idref/>
要素が値を与える共通の場所(少なくともSpring 2.0より前から)としてProxyFactoryBean
定義におけるAOP interceptorsの設定があります*10。インターセプターのidをミスタイプせずに指定する場合に<idref/>
要素を使います。
References to other beans (collaborators)
ref
要素は<constructor-arg/>
や<property/>
定義要素内の最後の要素(final element)です。ここにはコンテナ管理下の別のbean(協調オブジェクト)への参照となるbeanプロパティを指定します。参照されるbeanはプロパティに設定するbeanの依存性で、プロパティ設定前に必要に応じて初期化されます。(もし協調オブジェクトがシングルトンビーンの場合、既にコンテナが初期化しています)すべての参照は最終的に別オブジェクトへの参照になります。スコープと検証はbean
, local
, parent
属性に他オブジェクトのid/nameを指定しているかどうかに依存します。
<ref/>
タグのbean
属性にターゲットビーンを指定するのが最も一般的な形式で、同一コンテナもしくは親コンテナの任意のbeanへの参照を、同一XMLファイルにあるかどうかに関わらず、作成できます。bean
属性値は、ターゲットビーンのname
属性値の一つか、id
属性と同じで構いません。
<ref bean="someBean"/>
parent
属性でターゲットビーンを指定することで、現在のコンテナの親コンテナのビーンへの参照を作成できます。parent
属性値は、おおむねターゲットビーンのid
属性か、name
属性の内の一つのいずれか、と同じにします。また、ターゲットビーンは親コンテナでの存在が必須です。このビーン参照は主に、コンテナ階層の親ビーンと同一名を持つプロキシとして親コンテナ内の既存ビーンをラップしたい場合などに使用します。
<!-- 親コンテキストの設定ファイル --> <bean id="accountService" class="com.foo.SimpleAccountService"> <!-- insert dependencies as required as here --> </bean>
<!-- 子コンテキストの設定ファイル --> <bean id="accountService" <!-- 親ビーンと同一名のビーン --> class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref parent="accountService"/> <!-- 親のaccountServiceを参照していることに注意 --> </property> <!-- insert other configuration and dependencies as required here --> </bean>
ref
要素のlocal
属性は4.0 beans xsdではサポートしません。その理由は、標準的なbean
参照を通して値を提供することはもう無いためです。4.0スキーマへのアップグレード時には単に既存のidref local
をidref bean
に変更します。
Inner beans
<property/>
や<constructor-arg/>
要素内の<bean/>
要素はいわゆる内部ビーン(inner bean)を定義します。
<bean id="outer" class="..."> <!-- ターゲットビーンへの参照ではなく、インラインでターゲットビーンを定義しています --> <property name="target"> <bean class="com.example.Person"> <!-- this is the inner bean --> <property name="name" value="Fiona Apple"/> <property name="age" value="25"/> </bean> </property> </bean>
内部ビーン定義はidやname定義を必須とせず、コンテナはそれらの値を無視します。scope
タグも同様に無視します。内部ビーンは常に無名(anonymous)であり常に外部ビーンを伴って作られます。エンクロージングビーンでは無い他の協調ビーンへは内部ビーンをインジェクト出来ません。
Collections
<list/>
, <set/>
, <map/>
, <props/>
要素には、JavaのCollection
型であるList
, Set
, Map
, Properties
などの引数とプロパティを設定します。
<bean id="moreComplexObject" class="example.ComplexObject"> <!-- results in a setAdminEmails(java.util.Properties) call --> <property name="adminEmails"> <props> <prop key="administrator">administrator@example.org</prop> <prop key="support">support@example.org</prop> <prop key="development">development@example.org</prop> </props> </property> <!-- results in a setSomeList(java.util.List) call --> <property name="someList"> <list> <value>a list element followed by a reference</value> <ref bean="myDataSource" /> </list> </property> <!-- results in a setSomeMap(java.util.Map) call --> <property name="someMap"> <map> <entry key="an entry" value="just some string"/> <entry key ="a ref" value-ref="myDataSource"/> </map> </property> <!-- results in a setSomeSet(java.util.Set) call --> <property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource" /> </set> </property> </bean>
mapのkeyやvalueの値、もしくは、setの値には、以下の要素を任意個数設定できます。
bean | ref | idref | list | set | map | props | value | null
Collection merging
Springコンテナはコレクションのマージ(merging)をサポートします。アプリケーション開発者は親要素として<list/>
, <map/>
, <set/>
, <props/>
を定義し、子要素の<list/>
, <map/>
, <set/>
, <props/>
は親コレクションから値を引き継いでオーバーライドします。つまり、子側のコレクションの値は親と子のコレクション要素をマージしたものとなり、子のコレクション要素は親の値をオーバーライドします。
このセクションでは親-子間ビーンのマージについての説明です。もし親-子間のビーン定義に詳しくない場合はこれ以降を読み進める前に関連セクションを参照することをお勧めします。
以下はコレクションマージの例です。
<beans> <bean id="parent" abstract="true" class="example.ComplexObject"> <property name="adminEmails"> <props> <prop key="administrator">administrator@example.com</prop> <prop key="support">support@example.com</prop> </props> </property> </bean> <bean id="child" parent="parent"> <property name="adminEmails"> <!-- the merge is specified on the child collection definition --> <props merge="true"> <prop key="sales">sales@example.com</prop> <prop key="support">support@example.co.uk</prop> </props> </property> </bean> <beans>
child
ビーン定義のadminEmails
プロパティの<props/>
要素のmerge=true
属性を使用していることに注意してください。コンテナがchild
ビーンを解決して初期化する時、結果となるインスタンスはadminEmails
Properties
コレクションを持ち、そのコレクションは親のadminEmails
コレクションと子のadminEmails
コレクションをマージしたものになります。
administrator=administrator@example.com sales=sales@example.com support=support@example.co.uk
子のProperties
コレクションの値は親の<props/>
からすべてのプロパティ要素を引き継ぎ、support
に対応する値は親コレクションの値をオーバーライドします。
マージの振る舞いは<list/>
, <map/>
, <set/>
では同様です。<list/>
要素のある特定ケースでは、List
のコレクション型に依存し、値のordered
コレクションの概念が維持されます。親の値は子のすべての値よりも前になります。Map
, Set
, Properties
の場合、順序は存在しません。つまり、コンテナが内部的に使用するMap
, Set
, Properties
実装のコレクション型に影響を受けて順序が無い、ということです*11。
Limitations of collection merging
異なる型同士(たとえばMap
とList
)のマージは出来ず、もし試みた場合には適当なException
がスローされます。merge
属性は、従側・被継承先・子定義、にのみ指定可能です。親コレクション定義へのmerge
属性指定は冗長でありマージは行われません。
Strongly-typed collection
Java 5のジェネリック型の導入により強い型付コレクション(strongly typed collections)が使用可能になりました。これにより例えば、String
要素のみのCollection
型が宣言可能です。Springで強い型付Collection
をビーンに依存性注入する場合、Collection
に追加する前に強い型付Collcetion
インスタンスの要素を適切な型へと変換するなどの、Springの型変換サポートを使用できます。
public class Foo { private Map<String, Float> accounts; public void setAccounts(Map<String, Float> accounts) { this.accounts = accounts; } }
<beans> <bean id="foo" class="x.y.Foo"> <property name="accounts"> <map> <entry key="one" value="9.99"/> <entry key="two" value="2.75"/> <entry key="six" value="3.99"/> </map> </property> </bean> </beans>
foo
ビーンのaccounts
プロパティがインジェクション用に設定されており、強い型付のMap<String, Float>
の要素型に関するジェネリクス情報がリフレクションで利用可能です。Spring型変換は、値の要素はFloat
型と解釈し、文字列値9.99
, 2.75
, 3.99
はFloat
型に変換されます。
Null and empty string values
Springはプロパティの空要素と空のStrings
を扱えます。以下のXML設定メタデータはEメールプロパティに空String
("")を設定しています。
<bean class="ExampleBean"> <property name="email" value=""/> </bean>
上記の例は下記のJavaコードと同等です。
exampleBean.setEmail("")
<null/>
要素はnull
値です。例えば、
<bean class="ExampleBean"> <property name="email"> <null/> </property> </bean>
上記の設定は以下のJavaコードと同等です。
exampleBean.setEmail(null)
XML shortcut with the p-namespace
p名前空間は、bean
要素の属性を記述するのに、ネストした<property/>
要素の代わりに、プロパティ値およびまたは協調ビーンを記述するのに使用します。
SpringはXML Schema定義に基づいた名前空間による拡張設定フォーマットをサポートします。この章で説明しているbeans
設定フォーマットはXML Schemaドキュメントに定義されています。ただし、p名前空間はXSDファイルに定義されておらず、Springコアにのみ存在します。
以下の二つの例は同一の結果をもたらし、前者は標準的なXMLフォーマット、後者はp名前空間を使用しています。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="classic" class="com.example.ExampleBean"> <property name="email" value="foo@bar.com"/> </bean> <bean name="p-namespace" class="com.example.ExampleBean" p:email="foo@bar.com"/> </beans>
上記の例はbean定義のemailにp名前空間を使用しています。これはSpringに対しプロパティ宣言を含めるよう指示するものです。前述の通り、p名前空間はスキーマ定義を持たないので、プロパティ名に属性名を設定可能です。
次の例は別のbeanへの参照を持つ二つのbean定義があります。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="john-classic" class="com.example.Person"> <property name="name" value="John Doe"/> <property name="spouse" ref="jane"/> </bean> <bean name="john-modern" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/> <bean name="jane" class="com.example.Person"> <property name="name" value="Jane Doe"/> </bean> </beans>
上記の通り、この例はp名前空間を使用するプロパティ以外も含んでおり、また、プロパティ参照を宣言する特殊なフォーマットも使用しています。一つ目のビーン定義がjohn
からjane
への参照を作るのに<property name="spouse" ref="jane"/>
を使用しているのに対し、二つ目は同一のことをするのにp:spouse-ref="jane"
属性を使用しています。この例では、spouse
はプロパティ名であり-ref
部分は単純な値ではなく別のビーンへの参照なことを示します。
p名前空間は標準XMLフォーマットほど柔軟ではありません。たとえば、プロパティ参照宣言フォーマットはRef
で終わるプロパティと衝突しますが、標準XMLフォーマットはそうなりません。我々の推奨事項としては、XMLの作成を慎重に行い、チームメンバとコミュニケーションを取り、同時に異なる記法が混ざったXMLドキュメントを生成しないように心がけてください。
XML shortcut with the c-namespace
the section called “XML shortcut with the p-namespace”と同様に、c-namespaceはSpring 3.1で新規に導入されたもので、コンストラクタ引数の設定にconstructor-arg
要素のネストではなくインライン属性を使用可能にするものです。
the section called “Constructor-based dependency injection”のサンプルをc:
名前空間で書き直してみます。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> <!-- traditional declaration --> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> <constructor-arg value="foo@bar.com"/> </bean> <!-- c-namespace declaration --> <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/> </beans>
c:
名前空間は名前でコンストラクタ引数を設定するのにp:
と同様な規則(ビーン参照には-ref
プレフィクスを付与)で使えます。また同様に、XSDスキーマで定義されていないにも関わらず(ただしSpringコア内には存在する)宣言が必要です*12。
レアケースとしてコンストラクタ引数名が利用可能でない場合(主にデバッグ情報無しでバイトコードがコンパイル済みの場合)、引数インデックスをプランBとして使えます。
<!-- c-namespace index declaration --> <bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>
XMLの文法に従い、インデックス表記はXML属性名が数字で開始出来ない(IDEによってはOKだが)ので_
をプレフィクスにする必要があります。
実際にやってみると、so unless one really needs to*13、コンストラクタ解決メカニズムは引数マッチングにおいて極めて効果的ですが、我々は設定においては名前表記を使用することを推奨します。
Compound property names
ビーンプロパティ設定時にネストもしくはコンパウンドなプロパティ名を使用可能です。ただし、最後のプロパティ名を除いたパスの全コンポーネントがnull
でない場合に限ります。以下のビーン定義を例に取ります。
<bean id="foo" class="foo.Bar"> <property name="fred.bob.sammy" value="123" /> </bean>
foo
ビーンはfred
プロパティを持ち、fred
はbob
プロパティを持ち、bob
はsammy
プロパティを持ち、最後のsammy
プロパティには123
が設定されます。これが正しく動作するには、foo
のfred
プロパティとfred
のbob
プロパティがビーン生成後に非null
でなければならず、そうでない場合はNullPointerException
がスローされます。
5.4.3 Using depends-on
あるビーンが別のビーンの依存性である、とはつまり、あるビーンが別のビーンのプロパティとしてセットされる、ということです。通常、それをXML設定メタデータで実現するには<ref/>
要素を使います。しかし、ビーン間の依存性がそれほど直接的でない場合があります。たとえば、データベースドライバー設定などでは、クラスのstaticイニシャライザが実行されている必要があります。depends-on
属性は、その要素を使用するビーンが初期化される前に、明示的に一つ以上のビーンが初期化済みなことを強制します。以下の例は単一のビーン上で依存性を表現するのにdepends-on
属性を使用しています。
<bean id="beanOne" class="ExampleBean" depends-on="manager"/> <bean id="manager" class="ManagerBean" />
複数ビーンへの依存性を表現するには、depends-on
属性に、カンマ・空白・セミコロンをデリミタとして、ビーン名のリストを指定します。
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"> <property name="manager" ref="manager" /> </bean> <bean id="manager" class="ManagerBean" /> <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
ビーン定義のdepends-on
属性は、依存性初期化時とシングルトンビーンの場合のみ依存性破棄時の、両方に指定可能です*14。depends-on
を定義する依存元ビーンが先に破棄され、それから依存先のビーンが破棄されます。depends-on
はシャットダウン順序を制御するのにも使えます。
5.4.4 Lazy-initialized beans
デフォルトでは、ApplicationContext
の実装は初期化処理の一部としてすべてのシングルトンビーンの設定とeagerly createをします。一般的には、事前インスタンス化の方が望ましく、その理由は環境や周辺環境のエラーが、数時間や数日後ではなく、直ちに発見されるためです。デフォルトの振る舞いが望ましくない場合、ビーン定義に遅延初期化とマーキングすることでシングルトンビーンの事前初期化を避けられます。遅延初期化ビーンはIoCコンテナに対してスタートアップ時ではなく初回リクエスト時にビーンインスタンスを生成するよう指示します。
XMLでは、この振る舞いは<bean/>
要素のlazy-init
で制御します。
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> <bean name="not.lazy" class="com.foo.AnotherBean"/>
上記の設定をApplicationContext
が処理するとき、lazy
ビーンはApplicationContext
スタートアップ時にeagerlyな事前初期化をされず、一方、not.lazy
ビーンはeagerlyに事前初期化されます。
ただし、遅延初期化では無いシングルトンビーンの依存性が遅延初期化ビーンの場合、ApplicationContext
はスタートアップ時に遅延初期化ビーンを生成します。その理由は、シングルトンの依存性を満たす必要があるためです。遅延初期化ビーンは、遅延初期化では無いシングルトンビーンにインジェクトされます。
コンテナレベルで遅延初期化を制御するには<beans/>
要素のdefault-lazy-init
属性を使用します。
<beans default-lazy-init="true"> <!-- no beans will be pre-instantiated... --> </beans>
5.4.5 Autowiring collaborators
Springコンテナは協調ビーン間の関連をオートワイヤ(autowire)できます。SpringがApplicationContext
の内容を調べ、ビーンの協調ビーンを自動的に解決します。オートワイヤには以下の利点があります。
- オートワイヤはプロパティやコンストラクタ引数指定の手間を著しく削減します。(この章の別の個所で説明するビーンテンプレート(bean template)などのメカニズムもこの点で役に立ちます)
- オートワイヤはオブジェクトを作成しながら設定を更新可能です*15。たとえば、クラスに依存性を追加する時に、設定を更新することなく依存性を自動的に満たすことが出来ます。こうしたオートワイヤは特に開発中には有効で、コードベースが安定した後で明示的な記述に移行することも可能です。
XML設定メタデータを使用する場合、<bean/>
要素のautowire
属性にビーン定義のオートワイヤモードを指定します。オートワイヤには5つのモードがあります。ビーンごとにオートワイヤのモードを選択できます。
Table 5.2. Autowiring modes
Mode | Explanation |
---|---|
no | (デフォルト)オートワイヤ無効。ビーン参照はref 要素を介してのみ定義可能。大規模開発ではデフォルト設定の変更は非推奨で、その理由は、明示的に協調ビーンを指定する方が確実に制御出来るためです。設定は、ある程度は、システムの構造を文書化します。 |
byName | プロパティ名によるオートワイヤ。Springはオートワイヤ対象のプロパティと同名のビーンを探します。たとえば、ビーン定義のオートワイヤがby nameに設定され、masterプロパティ(setMaster(...)メソッド)を持つ場合、Springはmaster という名前のビーン定義を探し、それをプロパティにセットします。 |
byType | プロパティの型のビーンが実際にコンテナに存在する場合、プロパティをオートワイヤ可能にします。候補が一つ以上存在する場合、そのビーンにはbyTypeオートワイヤは使用できない事を示す致命的な例外がスローされます。マッチするビーンが無い場合、何も起こらず、プロパティには何もセットされません。 |
constructor | byTypeと似ており、こちらはコンストラクタ引数に適用されます。ただし、コンテナ内にコンストラクタ引数型のビーンが一つに限定されない場合、致命的エラーが発生します。 |
byTypeやconstructorオートワイヤモードでは配列と型付コレクションをワイヤリング可能です。この場合、期待型とマッチするコンテナ内のすべてのオートワイヤ候補が依存性を満たすのに使われます。キーの期待型がString
の場合には強い型付Mapをオートワイヤ可能です。オートワイヤされるMapの値は期待型にマッチするすべてのビーンインスタンスで構成され、Mapのキーはビーン名相当になります。
オートワイヤ完了後の依存性チェックとオートワイヤを組み合わせることが出来ます。
Limitations and disadvantages of autowiring
オートワイヤをプロジェクトで常に使うのであれば上手く動作します。もしオートワイヤを常に使うわけではないと、開発者は一つか二つのビーン定義を結びつけるのに困惑することになるでしょう。
オートワイヤの制限と欠点について説明します。
property
とconstructor-arg
の明示的な依存性は常にオートワイヤをオーバーライドします。プリミティブ・Strings
・Classes
(とsimple propertiesの配列)などの、いわゆるsimple propertiesはオートワイヤできません。これは設計上の制限です。- オーバーライドは明示的な指定よりもやれることは少ないです*16。上述の表で説明したとおり、Springは予期せぬ結果となりうる曖昧なケースを推測することを注意深く避けようとはしていますが、Spring管理下オブジェクト間の関連を明示的に文書化することはもうしていません。
- ワイヤリングの情報はSpringコンテナのドキュメントを生成するツールでは利用不可能です*17。
- コンテナ内の複数ビーン定義がオートワイヤ対象のセッターメソッドやコンストラクタ引数の型にマッチ可能です。配列・コレクション・Mapでは、これは問題というわけではありません。ただし、単一値(single value)を期待する依存性の場合、曖昧さは一意に解決されません(not arbitrarily resolved)。利用可能でユニークなビーン定義が無い場合、例外をスローします。
いくつかの別の選択肢があります。
- 明示的な記述を使うようにしてオートワイヤをやめる。
- 次セクションで述べるように
autowire-candidate
属性にfalse
を設定してビーン定義のオートワイヤをオフにする。 <bean/>
要素のprimary
属性をtrue
に設定して一意な候補(primary candidate)としてシングルビーン定義を割り当てる。- Section 5.9, “Annotation-based container configuration”で説明する、アノテーションによる設定を利用して、より細かい制御を実装する。
Excluding a bean from autowiring
ビーンごとに、オートワイヤからそのビーンを除外可能です。SpringのXML形式では、<bean/>
要素のautowire-candidate
属性にfalse
をセットすることで、コンテナは指定したビーン定義をオートワイヤで利用不可能にします(@Autowired
などアノテーションによる設定も含む)。
また、ビーン名のパターンマッチングによるオートワイヤ候補に制限をかけられます。<beans/>
要素のトップレベルにはdefault-autowire-candidates
属性内に一つ以上のパターンを設定可能です。たとえば、Repositoryで名前が終わるすべてのビーンに対してオートワイヤ候補を制限するには、*Repository
を設定します。複数パターンを設定するには、カンマ区切りで定義します。ビーン定義のautowire-candidate
属性のtrue
もしくはfalse
の明示的な指定は常に先に行われるため、そうしたビーンでは、パターンマッチングルールは適用されません。
これらの設定はオートワイヤで別のビーンにインジェクトしたくないビーンがある場合に役に立ちます。これはつまり、除外したビーン自体はオートワイヤで設定が行われないということです。もっと言うと、そのビーン自身が他ビーンをオートワイヤする候補にならない、ということです。
*1:are supplied out-of-the-box with Springが原文。Springがコンフィグレスを提供します、くらいの意味とは思うが…ちょっと自信が無い
*2:by providing a small amount of XML configuration to declaratively enable support for these additional metadata formats.が原文。なんかうまく訳せないので雰囲気訳
*3:collaborating objectsが原文。collaborateてのは二つ以上のオブジェクトが参照関係を持って動作する様を示すのに使われるが、ここでは直訳の『協調』を当てる
*4:appropriate setters and getters modeled after the properties in the containerが原文。 modeled afterを手本と訳したわけだが、『コンテナのプロパティを手本』とは一体何なのか自分でもわからない……
*5:When another bean is referenced, the type is known, and matching can occurが原文。良く分からん。型が既知であればマッチング可能、って意味かと思ったけど、matching can occurだからなぁ。
*6:ぐぐった感じ、プリミティブ型のラッパークラスとStringを指すらしい
*7:ここだけど、pre-instantiatedとlazy-initializeが逆になってる気がするんだよなぁ……
*8:なんで同じ型だとダメなのだろうか? 確かに一般的にはstaticファクトリーメソッドは生成するクラスとは別のクラスにあることが殆どだとは思うが……
*9:since it does not provide value over a regular bean reference anymore.が原文。なんか歴史的経緯があるようだけど、俺はその経緯を知らないので、上手く訳せてる自信が無い
*10:A common place (at least in versions earlier than Spring 2.0) where the
*11:Hence no ordering semantics are in effect for the collection types that underlie the associated Map, Set, and Properties implementation types that the container uses internally.が原文。no ordering semantics are in effect forのあたりがイマイチ上手く訳せず。要するに、順序持たない実装使えば設定もそれに従うよ、というだけの意味だとは思うが
*12:And just as well, it needs to be declared even though it is not defined in an XSD schemaが原文。スキーマが云々のところ、あんまよく理解せずに直訳してるだけなんで、なんか意味不明なこと書いてたら申し訳ない
*13:~する必要がない限りは、って感じだが、oneが何を指してるのかよーわからなんだ……このあとにname notationを推奨するってあるんで、インデックス表記はオススメしませんよ、って事だとは思うけど。
*14:initialization time dependency and, in the case of singleton beans only, a corresponding destroy time dependency.が原文。~time~がイマイチ何を指してるのか分からんのでテキトーな訳になった
*15:Autowiring can update a configuration as your objects evolve.が原文。as your objects evolveは、この後にあるように、コードを開発中でアレコレ書き換えている段階でも~というニュアンスと思われる。
*16:less exact thanが原文。こういうニュアンスで良いんですかね?
*17:Wiring information may not be available to tools that may generate documentation from a Spring container.が原文。何のことか良く分からない……