読者です 読者をやめる 読者になる 読者になる

kagamihogeの日記

kagamihogeの日記です。

Spring Framework Reference Documentation 4.1.5のIII. Core Technologies 5.4.5までをテキトーに訳した

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などをセットアップしなくてもテスト時にインスタンス同士を結び付けるのは容易です)。テストに関する章を読めばおそらく納得して頂けるかと思います。

5. The IoC container

5.1 Introduction to the Spring IoC container and beans

この章ではInversion of Control (IoC)のSpring Frameworkでの実装について解説します。なお、IoCdependency injection (DI)とも呼びます。IoCは依存オブジェクトの定義を処理します。依存オブジェクトとは自身が共に動作する他のオブジェクトのことで、ファクトリメソッドが生成したり戻り値として返すオブジェクトインスタンスを、プロパティ・ファクトリメソッドへの引数・コンストラクタ引数、などを通じて設定します。コンテナは生成したbeanをそれらの依存オブジェクトにinjectsします。これは、Service Locatorパターンなどのメカニズムやクラスをコンストラクタで直接生成して依存オブジェクトの初期化や指定の制御をbean自身が行うのに比べると、基本的には処理順序が反転しており、よって、Inversion of Control (IoC)(制御の反転)と呼ばれています。

org.springframework.beansorg.springframework.contextパッケージがSpring FrameworkIoCコンテナの基礎部分です。BeanFactoryインタフェースは任意の型のオブジェクトを管理する機能を持つ拡張設定を提供します。ApplicationContextBeanFactoryのサブインタフェースです。このサブインタフェースはSpring AOP機能を使用して連携を容易にするもので、メッセージリソースハンドリング(国際化に使用)・イベント発行・webアプリケーションで使用するWebApplicationContextなどのアプリケーションレイヤー固有のコンテキスト、に使用します。

簡潔に言えば、BeanFactoryは基本機能と設定フレームワークを提供し、ApplicationContextエンタープライズ固有の機能を追加したものです。ApplicationContextBeanFactoryの完全な上位集合(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の初期化・設定・アセンブルの役割を担います。コンテナは設定メタデータを読み込み、初期化・設定・アセンブリ対象オブジェクトの命令を取得します。設定メタデータは、XMLJavaアノテーション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

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)、StrutsActionなどのプレゼンテーションオブジェクト、HibernateSessionFactoriesなどのインフラオブジェクト、JMSQueues、などです。一般的でないのは、コンテナには細粒度のドメインオブジェクトは設定しません。その理由は、ドメインオブジェクトの生成とロードの責務はDAOとビジネスロジックに負わせるのが自然なためです。しかし、IoCコンテナの制御下でない場所で生成したオブジェクトを設定するには、AspectJと共にSpring integrationを使用します。Using AspectJ to dependency-inject domain objects with Springを参照してください。

以下の例は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="..." 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クラスと二つのデータアクセスオブジェクト(JPAO/Rマッピングベースの)JpaAccountDaoJpaItemDaoで構成しています。property name要素はJavaBeanのプロパティ名を参照し、ref要素は別のbean定義を参照します。idref要素間のリンクは協調オブジェクト間の依存性を表しています。オブジェクトの依存性の設定の詳細については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.xmlthemeSource.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を取得します。DefaultListableBeanFactoryregisterSingleton(..)registerBeanDefinition(..)メソッドを使用して登録を行います。しかし、一般的なアプリケーションではメタデータbean定義のみで十分です。

5.3.1 Naming beans

すべてのbeanは一つ以上の識別子を持ちます。識別子はbeanをホストするコンテナ内で一意にする必要があります。beanは通常一つだけ識別子を持ちますが、一つ以上を持たせたい場合は別名が作れます。

XMLベース設定メタデータでは、bean識別子にはidname属性の両方またはいずれか一方を指定します。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 beansautowiring 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 injectionSetter-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) {
        // ...
    }

}

潜在的な曖昧さは無く、BarBazクラスは互いに継承関係に無いと仮定します。このとき以下の設定は正しく動作し、<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が生成および初期化されます。設定メタデータは、XMLJavaコード・アノテーションなどで指定します。
  • 各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 methodInitializingBean 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 serviceStringからプロパティや引数の実際の型への変更を行います。

<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 IDEASpring 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 localidref 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 localidref 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/>要素には、JavaCollection型である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

異なる型同士(たとえばMapList)のマージは出来ず、もし試みた場合には適当な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.99Float型に変換されます。

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プロパティを持ち、fredbobプロパティを持ち、bobsammyプロパティを持ち、最後のsammyプロパティには123が設定されます。これが正しく動作するには、foofredプロパティとfredbobプロパティがビーン生成後に非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属性は、依存性初期化時とシングルトンビーンの場合のみ依存性破棄時の、両方に指定可能です*14depends-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と似ており、こちらはコンストラクタ引数に適用されます。ただし、コンテナ内にコンストラクタ引数型のビーンが一つに限定されない場合、致命的エラーが発生します。

byTypeconstructorオートワイヤモードでは配列と型付コレクションをワイヤリング可能です。この場合、期待型とマッチするコンテナ内のすべてのオートワイヤ候補が依存性を満たすのに使われます。キーの期待型がStringの場合には強い型付Mapをオートワイヤ可能です。オートワイヤされるMapの値は期待型にマッチするすべてのビーンインスタンスで構成され、Mapのキーはビーン名相当になります。

オートワイヤ完了後の依存性チェックとオートワイヤを組み合わせることが出来ます。

Limitations and disadvantages of autowiring

オートワイヤをプロジェクトで常に使うのであれば上手く動作します。もしオートワイヤを常に使うわけではないと、開発者は一つか二つのビーン定義を結びつけるのに困惑することになるでしょう。

オートワイヤの制限と欠点について説明します。

  • propertyconstructor-argの明示的な依存性は常にオートワイヤをオーバーライドします。プリミティブ・StringsClasses(と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 element brings value is in the configuration of AOP interceptors in a ProxyFactoryBean bean definition. が原文。この場合、bringってどう訳せばいいのやら……

*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.が原文。何のことか良く分からない……