Bean の概要

Spring IoC コンテナーは、1 つ以上の Bean を管理します。これらの Bean は、コンテナーに提供する構成メタデータを使用して作成されます(たとえば、XML <bean/> 定義の形式で)。

コンテナー自体の中では、これらの Bean 定義は BeanDefinition オブジェクトとして表され、(他の情報とともに)次のメタデータを含んでいます:

  • パッケージ修飾クラス名: 通常、定義されている Bean の実際の実装クラス。

  • Bean 動作構成要素。Bean がコンテナー内でどのように動作するかを指定します(スコープ、ライフサイクルコールバックなど)。

  • Bean が機能するために必要な他の Bean への参照。これらの参照は、コラボレーターまたは依存関係とも呼ばれます。

  • 新しく作成されたオブジェクトに設定する他の構成設定。たとえば、プールのサイズ制限や、接続プールを管理する Bean で使用する接続数など。

このメタデータは、各 Bean 定義を構成する一連のプロパティに変換されます。次の表で、これらのプロパティについて説明します。

表 1: Bean 定義
プロパティ 説明…

クラス

Bean のインスタンス化

名前

Bean の命名

スコープ

Bean スコープ

コンストラクター引数

依存性注入

プロパティ

依存性注入

オートワイヤーモード

オートワイヤーのコラボレーター

遅延初期化モード

遅延初期化された Bean

初期化メソッド

初期化コールバック

破棄メソッド

破棄コールバック

特定の Bean を作成する方法に関する情報を含む Bean 定義に加えて、ApplicationContext 実装では、コンテナーの外部で(ユーザーによって)作成された既存のオブジェクトの登録も可能です。これは、DefaultListableBeanFactory 実装を返す getBeanFactory() メソッドを介して ApplicationContext の BeanFactory にアクセスすることによって行われます。DefaultListableBeanFactory は、registerSingleton(..) および registerBeanDefinition(..) メソッドを介してこの登録をサポートします。ただし、一般的なアプリケーションは、通常の Bean 定義メタデータを介して定義された Bean でのみ機能します。

Bean のメタデータと手動で提供されたシングルトンインスタンスは、コンテナーがオートワイヤーやその他のイントロスペクションの段階で適切に判断できるように、できるだけ早く登録する必要があります。既存のメタデータおよび既存のシングルトンインスタンスの上書きはある程度サポートされていますが、実行時の新規 Bean の登録 (ファクトリへのライブアクセスと同時に) は公式にはサポートされておらず、同時アクセス例外、Bean コンテナーでの矛盾した状態、その両方につながる可能性があります。

Bean のオーバーライド

Bean のオーバーライドは、すでに割り当てられている識別子を使用して Bean が登録されたときに発生します。Bean のオーバーライドは可能ですが、構成が読みにくくなるため、この機能は将来のリリースで廃止される予定です。

Bean オーバーライドを完全に無効にするには、ApplicationContext がリフレッシュされる前に、allowBeanDefinitionOverriding フラグを false に設定します。このような設定では、Bean オーバーライドが使用されると例外がスローされます。

デフォルトでは、コンテナーは INFO レベルでの Bean オーバーライドをすべてログに記録するため、それに応じて構成を調整できます。推奨はされませんが、allowBeanDefinitionOverriding フラグを true に設定することで、これらのログを無音にすることができます。

Java 構成

Java 構成を使用する場合、@Bean メソッドの戻り値の型がその Bean クラスと一致する限り、対応する @Bean メソッドは常に、同じコンポーネント名を持つスキャンされた Bean クラスを暗黙的にオーバーライドします。これは、コンテナーが Bean クラスの事前宣言されたコンストラクターを優先して @Bean ファクトリメソッドを呼び出すことを意味します。

Bean の命名

すべての Bean には 1 つ以上の識別子があります。これらの識別子は、Bean をホストするコンテナー内で一意である必要があります。Bean には通常 1 つの識別子しかありません。ただし、複数必要な場合は、余分なものをエイリアスと見なすことができます。

XML ベースの構成メタデータでは、id 属性、name 属性、またはその両方を使用して Bean 識別子を指定します。id 属性を使用すると、id を 1 つだけ指定できます。通常、これらの名前は英数字 ('myBean'、'someService' など) ですが、特殊文字を含めることもできます。Bean に他のエイリアスを導入する場合は、name 属性で、コンマ (,)、セミコロン (;)、または空白で区切って指定することもできます。id 属性は xsd:string 型として定義されていますが、Bean と id の一意性はコンテナーによって強制されますが、XML パーサーによって強制されるわけではありません。

Bean に name または id を指定する必要はありません。name または id を明示的に指定しない場合、コンテナーはその Bean に一意の名前を生成します。ただし、ref 要素またはサービスロケータースタイルの検索を使用して、その Bean を名前で参照する場合は、名前を指定する必要があります。名前を指定しない理由は、内部 Beanオートワイヤリングコラボレーターの使用に関連しています。

Bean の命名規則

規則は、Bean の命名時にインスタンスフィールド名に標準の Java 規則を使用することです。つまり、Bean 名は小文字で始まり、そこからキャメルケースになります。そのような名前の例には、accountManageraccountServiceuserDaologinController などがあります。

Bean に一貫して名前を付けると、構成が読みやすくなり、理解しやすくなります。また、Spring AOP を使用すると、名前で関連付けられた一連の Bean にアドバイスを適用するときに非常に役立ちます。

クラスパスでコンポーネントをスキャンすると、Spring は前述のルールに従って、名前のないコンポーネントの Bean 名を生成します。基本的には、単純なクラス名を取得し、その最初の文字を小文字に変換します。ただし、複数の文字があり、最初の文字と 2 番目の文字の両方が大文字である(異常な)特殊なケースでは、元の大文字と小文字が保持されます。これらは、java.beans.Introspector.decapitalize (Spring がここで使用する)で定義されているものと同じルールです。

Bean 定義外の Bean のエイリアス

Bean 定義自体では、id 属性で指定された最大 1 つの名前と 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 を参照します。3 つの名前すべてが同じオブジェクトを参照するようにするには、次のエイリアス定義を構成メタデータに追加できます。

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

これで、各コンポーネントとメインアプリケーションは、一意で他の定義と競合しないことが保証された名前 (実質的に名前空間を作成) を使用して dataSource を参照できますが、参照先は同じ Bean になります。

Java 構成

Java 構成を使用する場合、@Bean アノテーションを使用してエイリアスを提供できます。詳細については、@Bean アノテーションの使用を参照してください。

Bean のインスタンス化

Bean 定義は、本質的に 1 つ以上のオブジェクトを作成するためのレシピです。コンテナーは、要求されると名前付き Bean のレシピを調べ、その Bean 定義によってカプセル化された構成メタデータを使用して、実際のオブジェクトを作成(または取得)します。

XML ベースの構成メタデータを使用する場合、<bean/> 要素の class 属性でインスタンス化されるオブジェクトの型(またはクラス)を指定します。通常、この class 属性(内部的には BeanDefinition インスタンスの Class プロパティ)は必須です。(例外については、インスタンスファクトリメソッドを使用したインスタンス化および Bean 定義の継承を参照してください) Class プロパティは、次の 2 つの方法のいずれかで使用できます。

  • 通常、new 演算子を使用した Java コードと多少同等の、コンストラクターをリフレクティブに呼び出すことにより、コンテナー自体が直接 Bean を作成する場合に構築される Bean クラスを指定します。

  • オブジェクトを作成するために呼び出される static ファクトリメソッドを含む実際のクラスを指定するには、あまり一般的ではないが、コンテナーがクラスで static ファクトリメソッドを呼び出して Bean を作成します。static ファクトリメソッドの呼び出しから返されるオブジェクト型は、同じクラスまたは完全に別のクラスです。

ネストされたクラス名

ネストされたクラスの Bean 定義を構成する場合は、ネストされたクラスのバイナリ名またはソース名のいずれかを使用できます。

例: com.example パッケージに SomeThing というクラスがあり、この SomeThing クラスに OtherThing という static ネストクラスがある場合、ドル記号($)またはドット(.)で区切ることができます。Bean 定義の class 属性の値は、com.example.SomeThing$OtherThing または com.example.SomeThing.OtherThing になります。

コンストラクターによるインスタンス化

コンストラクターアプローチによって Bean を作成すると、すべての通常クラスが Spring で使用でき、互換性があります。つまり、開発中のクラスは、特定のインターフェースを実装したり、特定の方法でコーディングしたりする必要はありません。Bean クラスを指定するだけで十分です。ただし、その特定の Bean に使用する IoC の型によっては、デフォルト(空の)コンストラクターが必要になる場合があります。

Spring IoC コンテナーは、管理したいほぼすべてのクラスを管理できます。真の JavaBeans の管理に限定されません。ほとんどの Spring ユーザーは、デフォルト(引数なし)コンストラクターと、コンテナー内のプロパティをモデルにした適切な setter および getter のみを備えた実際の JavaBeans を好みます。コンテナーには、よりエキゾチックな非 Bean スタイルのクラスを含めることもできます。たとえば、JavaBean 仕様に絶対に準拠していないレガシー接続プールを使用する必要がある場合、Spring もそれを管理できます。

XML ベースの構成メタデータを使用すると、Bean クラスを次のように指定できます。

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

コンストラクターに引数を提供し(必要な場合)、オブジェクトの構築後にオブジェクトインスタンスプロパティを設定するメカニズムの詳細については、依存関係の注入を参照してください。

コンストラクター引数の場合、コンテナーは複数のオーバーロードされたコンストラクターの中から対応するコンストラクターを選択できます。ただし、あいまいさを避けるために、コンストラクターシグネチャーはできるだけわかりやすくしておくことをお勧めします。

静的ファクトリメソッドを使用したインスタンス化

静的ファクトリメソッドを使用して作成する Bean を定義する場合、class 属性を使用して static ファクトリメソッドを含むクラスを指定し、factory-method という名前の属性を使用してファクトリメソッド自体の名前を指定します。このメソッドを(後述のオプションの引数を使用して)呼び出し、ライブオブジェクトを返すことができるはずです。ライブオブジェクトは、その後、コンストラクターを介して作成されたかのように扱われます。そのような Bean 定義の 1 つの用途は、レガシーコードで static ファクトリを呼び出すことです。

次の Bean 定義は、ファクトリメソッドを呼び出して Bean を作成することを指定しています。定義では、返されるオブジェクトの型(クラス)ではなく、ファクトリメソッドを含むクラスを指定します。この例では、createInstance() メソッドは static メソッドである必要があります。次の例は、ファクトリメソッドを指定する方法を示しています。

<bean id="clientService"
	class="examples.ClientService"
	factory-method="createInstance"/>

次の例は、前述の Bean 定義で機能するクラスを示しています。

  • Java

  • Kotlin

public class ClientService {
	private static ClientService clientService = new ClientService();
	private ClientService() {}

	public static ClientService createInstance() {
		return clientService;
	}
}
class ClientService private constructor() {
	companion object {
		private val clientService = ClientService()
		@JvmStatic
		fun createInstance() = clientService
	}
}

ファクトリメソッドに(オプションの)引数を提供し、オブジェクトがファクトリから返された後にオブジェクトインスタンスプロパティを設定するメカニズムの詳細については、依存関係と構成の詳細を参照してください。

ファクトリメソッド引数の場合、コンテナーは同じ名前の複数のオーバーロードメソッドの中から対応するメソッドを選択できます。ただし、あいまいさを避けるために、ファクトリメソッドシグネチャーはできるだけわかりやすくしておくことをお勧めします。

ファクトリメソッドのオーバーロードで問題となる典型的なケースは、mock メソッドのオーバーロードが多数ある Mockito です。可能な限り最も具体的な mock のバリアントを選択してください。

<bean id="clientService" class="org.mockito.Mockito" factory-method="mock">
	<constructor-arg type="java.lang.Class" value="examples.ClientService"/>
	<constructor-arg type="java.lang.String" value="clientService"/>
</bean>

インスタンスファクトリメソッドを使用したインスタンス化

静的ファクトリメソッドによるインスタンス化と同様に、インスタンスファクトリメソッドによるインスタンス化では、コンテナーから既存の Bean の非静的メソッドを呼び出して、新しい Bean を作成します。このメカニズムを使用するには、class 属性を空のままにし、factory-bean 属性で、オブジェクトを作成するために呼び出されるインスタンスメソッドを含む、現在の (または親または祖先) コンテナー内の Bean の名前を指定します。ファクトリメソッド自体の名前を factory-method 属性で設定します。次の例は、そのような Bean を構成する方法を示しています。

<!-- the factory bean, which contains a method called createClientServiceInstance() -->
<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"/>

次の例は、対応するクラスを示しています。

  • Java

  • Kotlin

public class DefaultServiceLocator {

	private static ClientService clientService = new ClientServiceImpl();

	public ClientService createClientServiceInstance() {
		return clientService;
	}
}
class DefaultServiceLocator {
	companion object {
		private val clientService = ClientServiceImpl()
	}
	fun createClientServiceInstance(): ClientService {
		return clientService
	}
}

次の例に示すように、1 つのファクトリクラスは複数のファクトリメソッドも保持できます。

<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"/>

次の例は、対応するクラスを示しています。

  • Java

  • Kotlin

public class DefaultServiceLocator {

	private static ClientService clientService = new ClientServiceImpl();

	private static AccountService accountService = new AccountServiceImpl();

	public ClientService createClientServiceInstance() {
		return clientService;
	}

	public AccountService createAccountServiceInstance() {
		return accountService;
	}
}
class DefaultServiceLocator {
	companion object {
		private val clientService = ClientServiceImpl()
		private val accountService = AccountServiceImpl()
	}

	fun createClientServiceInstance(): ClientService {
		return clientService
	}

	fun createAccountServiceInstance(): AccountService {
		return accountService
	}
}

このアプローチは、ファクトリ Bean 自体を依存性注入(DI)によって管理および構成できることを示しています。依存関係と構成の詳細を参照してください。

Spring ドキュメントでは、「ファクトリ Bean」とは、Spring コンテナー内で構成され、インスタンスまたは静的ファクトリメソッドを通じてオブジェクトを作成する Bean を指します。対照的に、FactoryBean (大文字に注意) は Spring 固有の FactoryBean 実装クラスを指します。

Bean の実行時型の決定

特定の Bean の実行時型を決定することは簡単ではありません。Bean メタデータ定義で指定されたクラスは、単なる初期クラス参照であり、宣言されたファクトリメソッドと組み合わされるか、FactoryBean クラスであり、Bean の実行時型が異なるか、インスタンスの場合はまったく設定されない可能性があります。レベルのファクトリメソッド(代わりに、指定された factory-bean 名を介して解決されます)。さらに、AOP プロキシは、ターゲット Bean の実際の型(実装されたインターフェースのみ)の限定的な公開で、インターフェースベースのプロキシで Bean インスタンスをラップする場合があります。

特定の Bean の実際の実行時型を調べるには、指定された Bean 名の BeanFactory.getType 呼び出しをお勧めします。これは、上記のすべてのケースを考慮に入れ、BeanFactory.getBean 呼び出しが同じ Bean 名に対して返すオブジェクトの型を返します。