Bean スコープ

Bean 定義を作成するとき、その Bean 定義によって定義されたクラスの実際のインスタンスを作成するためのレシピを作成します。Bean 定義がレシピであるという考え方は重要です。これは、クラスと同様に、単一のレシピから多くのオブジェクトインスタンスを作成できることを意味するためです。

特定の Bean 定義から作成されたオブジェクトにプラグインされるさまざまな依存関係と構成値を制御できるだけでなく、特定の Bean 定義から作成されたオブジェクトのスコープも制御できます。このアプローチは強力で柔軟です。なぜなら、Java クラスレベルでオブジェクトのスコープをベイク処理する代わりに、構成を通じて作成するオブジェクトのスコープを選択できるからです。Bean は、いくつかのスコープのいずれかにデプロイされるように定義できます。Spring Framework は 6 つのスコープをサポートしますが、そのうち 4 つは Web 対応の ApplicationContext を使用する場合にのみ使用可能です。カスタムスコープを作成することもできます。

次の表に、サポートされているスコープを示します。

表 1: Bean スコープ
スコープ 説明

singleton

(デフォルト)Spring IoC コンテナーごとに、単一の Bean 定義を単一のオブジェクトインスタンスにスコープします。

prototype

単一の Bean 定義を任意の数のオブジェクトインスタンスにスコープします。

request

単一の Bean 定義を単一の HTTP リクエストのライフサイクルにスコープします。つまり、各 HTTP リクエストには、単一の Bean 定義の背後から作成された Bean の独自のインスタンスがあります。Web 対応 Spring ApplicationContext のコンテキストでのみ有効です。

session

単一の Bean 定義を HTTP Session のライフサイクルにスコープします。Web 対応 Spring ApplicationContext のコンテキストでのみ有効です。

application

単一の Bean 定義を ServletContext のライフサイクルにスコープします。Web 対応 Spring ApplicationContext のコンテキストでのみ有効です。

websocket

単一の Bean 定義を WebSocket のライフサイクルにスコープします。Web 対応 Spring ApplicationContext のコンテキストでのみ有効です。

スレッドスコープは利用可能ですが、デフォルトでは登録されていません。詳細については、SimpleThreadScope (Javadoc) のドキュメントを参照してください。このスコープまたはその他のカスタムスコープを登録する方法については、カスタムスコープの使用を参照してください。

シングルトンスコープ

シングルトン Bean の 1 つの共有インスタンスのみが管理され、その Bean 定義に一致する 1 つ以上の ID を持つ Bean のすべてのリクエストにより、その 1 つの特定の Bean インスタンスが Spring コンテナーによって返されます。

別の言い方をすれば、Bean 定義を定義し、シングルトンとしてスコープされている場合、Spring IoC コンテナーは、その Bean 定義によって定義されたオブジェクトのインスタンスを 1 つだけ作成します。この単一のインスタンスは、そのようなシングルトン Bean のキャッシュに格納され、その名前付き Bean に対する以降のすべてのリクエストと参照は、キャッシュされたオブジェクトを返します。次の図は、シングルトンスコープの仕組みを示しています。

singleton

Spring のシングルトン Bean の概念は、Gang of Four(GoF)パターンブックで定義されているシングルトンパターンとは異なります。GoF シングルトンは、ClassLoader ごとに特定のクラスのインスタンスが 1 つだけ作成されるように、オブジェクトのスコープをハードコードします。Spring シングルトンの範囲は、コンテナーごとおよび Bean ごとと最もよく説明されています。つまり、単一の Spring コンテナー内の特定のクラスに対して 1 つの Bean を定義すると、Spring コンテナーはその Bean 定義によって定義されたクラスのインスタンスを 1 つだけ作成します。シングルトンスコープは、Spring のデフォルトスコープです。Bean を XML のシングルトンとして定義するには、次の例に示すように Bean を定義できます。

<bean id="accountService" class="com.something.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

プロトタイプスコープ

Bean デプロイの非シングルトンプロトタイプスコープでは、その特定の Bean のリクエストが行われるたびに、新しい Bean インスタンスが作成されます。つまり、Bean が別の Bean に注入されるか、コンテナーで getBean() メソッド呼び出しを介してリクエストされます。原則として、すべてのステートフル Bean にはプロトタイプスコープを使用し、ステートレス Bean にはシングルトンスコープを使用する必要があります。

次の図は、Spring プロトタイプスコープを示しています。

prototype

(典型的な DAO は会話状態を保持しないため、データアクセスオブジェクト(DAO)は通常、プロトタイプとして構成されません。シングルトンダイアグラムのコアを再利用する方が簡単でした)

次の例では、Bean を XML のプロトタイプとして定義しています。

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

他のスコープとは異なり、Spring はプロトタイプ Bean の完全なライフサイクルを管理しません。コンテナーは、プロトタイプオブジェクトをインスタンス化し、構成し、その他の方法で組み立ててクライアントに渡しますが、そのプロトタイプインスタンスのそれ以上の記録はありません。初期化ライフサイクルコールバックメソッドはスコープに関係なくすべてのオブジェクトで呼び出されますが、プロトタイプの場合、構成された破棄ライフサイクルコールバックは呼び出されません。クライアントコードは、プロトタイプスコープのオブジェクトをクリーンアップし、プロトタイプ Bean が保持する高負荷なリソースを解放する必要があります。Spring コンテナーがプロトタイプスコープの Bean によって保持されているリソースを解放できるようにするには、クリーンアップする必要がある Bean への参照を保持するカスタム Bean ポストプロセッサーを使用してみてください。

いくつかの点で、プロトタイプスコープの Bean に関する Spring コンテナーのロールは、Java new オペレーターに代わるものです。その時点以降のすべてのライフサイクル管理は、クライアントが処理する必要があります。(Spring コンテナー内の Bean のライフサイクルの詳細については、ライフサイクルコールバックを参照してください。)

プロトタイプ Bean 依存関係を持つシングルトン Bean

プロトタイプ Bean に依存するシングルトンスコープ Bean を使用する場合、インスタンス化時に依存関係が解決されることに注意してください。プロトタイプスコープの Bean をシングルトンスコープの Bean に依存性注入すると、新しいプロトタイプ Bean がインスタンス化され、シングルトン Bean に依存性注入されます。プロトタイプインスタンスは、シングルトンスコープの Bean に提供される唯一のインスタンスです。

ただし、シングルトンスコープの Bean で、実行時にプロトタイプスコープの Bean の新しいインスタンスを繰り返し取得するとします。プロトタイプスコープの Bean をシングルトン Bean に依存性注入することはできません。これは、Spring コンテナーがシングルトン Bean をインスタンス化し、その依存性を解決して注入するときに、その注入が 1 回だけ行われるためです。実行時にプロトタイプ Bean の新しいインスタンスが複数回必要な場合は、メソッドインジェクションを参照してください。

リクエスト、セッション、アプリケーション、WebSocket スコープ

requestsessionapplicationwebsocket スコープは、Web 対応の Spring ApplicationContext 実装(XmlWebApplicationContext など)を使用する場合にのみ使用できます。これらのスコープを ClassPathXmlApplicationContext などの通常の Spring IoC コンテナーで使用すると、不明な Bean スコープについて文句を言う IllegalStateException がスローされます。

Web の初期設定

requestsessionapplicationwebsocket レベルでの Bean のスコープ(Web スコープの Bean)をサポートするには、Bean を定義する前にいくつかのマイナーな初期構成が必要です。(この初期セットアップは、標準スコープ singleton および prototype には必要ありません。)

この初期設定を達成する方法は、特定のサーブレット環境によって異なります。

Spring Web MVC 内の Spring DispatcherServlet によって処理されるリクエスト内で、スコープ付き Bean にアクセスする場合、特別な設定は必要ありません。DispatcherServlet は、関連するすべての状態をすでに公開しています。

Spring の DispatcherServlet の外部で処理されるリクエストでサーブレット Web コンテナーを使用する場合 (たとえば、JSF を使用する場合)、org.springframework.web.context.request.RequestContextListenerServletRequestListener を登録する必要があります。これは、WebApplicationInitializer インターフェースを使用してプログラムで行うことができます。または、Web アプリケーションの web.xml ファイルに次の宣言を追加します。

<web-app>
	...
	<listener>
		<listener-class>
			org.springframework.web.context.request.RequestContextListener
		</listener-class>
	</listener>
	...
</web-app>

または、リスナーの設定に課題がある場合は、Spring の RequestContextFilter の使用を検討してください。フィルターマッピングは、周囲の Web アプリケーションの構成に依存するため、必要に応じて変更する必要があります。次のリストは、Web アプリケーションのフィルター部分を示しています。

<web-app>
	...
	<filter>
		<filter-name>requestContextFilter</filter-name>
		<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>requestContextFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	...
</web-app>

DispatcherServletRequestContextListenerRequestContextFilter はすべてまったく同じことを行います。つまり、HTTP リクエストオブジェクトを、そのリクエストを処理している Thread にバインドします。これにより、リクエストスコープおよびセッションスコープの Bean がチェーン呼び出しのさらに下で使用可能になります。

リクエストスコープ

Bean 定義の次の XML 構成を検討してください。

<bean id="loginAction" class="com.something.LoginAction" scope="request"/>

Spring コンテナーは、すべての HTTP リクエストの loginAction Bean 定義を使用して、LoginAction Bean の新しいインスタンスを作成します。つまり、loginAction Bean は、HTTP リクエストレベルでスコープされます。同じ loginAction Bean 定義から作成された他のインスタンスはこれらの状態の変化を認識しないため、作成されたインスタンスの内部状態を必要なだけ変更できます。これらは個々のリクエストに固有のものです。リクエストの処理が完了すると、リクエストのスコープにある Bean は破棄されます。

アノテーション駆動型コンポーネントまたは Java 構成を使用する場合、@RequestScope アノテーションを使用して、コンポーネントを request スコープに割り当てることができます。次の例は、その方法を示しています。

  • Java

  • Kotlin

@RequestScope
@Component
public class LoginAction {
	// ...
}
@RequestScope
@Component
class LoginAction {
	// ...
}

セッションスコープ

Bean 定義の次の XML 構成を検討してください。

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

Spring コンテナーは、単一の HTTP Session の存続期間に userPreferences Bean 定義を使用して、UserPreferences Bean の新しいインスタンスを作成します。言い換えると、userPreferences Bean は HTTP Session レベルで効果的にスコープされます。リクエストスコープ Bean と同様に、同じ userPreferences Bean 定義から作成されたインスタンスも使用している他の HTTP Session インスタンスはこれらの状態の変化を認識しないため、作成されるインスタンスの内部状態を必要なだけ変更できます。なぜなら、それらは個々の HTTP Session に特有です。HTTP Session が最終的に破棄されると、その特定の HTTP Session にスコープされた Bean も破棄されます。

アノテーション駆動型コンポーネントまたは Java 構成を使用する場合、@SessionScope アノテーションを使用して、コンポーネントを session スコープに割り当てることができます。

  • Java

  • Kotlin

@SessionScope
@Component
public class UserPreferences {
	// ...
}
@SessionScope
@Component
class UserPreferences {
	// ...
}

アプリケーションスコープ

Bean 定義の次の XML 構成を検討してください。

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>

Spring コンテナーは、Web アプリケーション全体に対して appPreferences Bean 定義を 1 回使用することにより、AppPreferences Bean の新しいインスタンスを作成します。つまり、appPreferences Bean は ServletContext レベルでスコープされ、通常の ServletContext 属性として保管されます。これは Spring シングルトン Bean にいくぶん似ていますが、2 つの重要な点で異なります。Spring ApplicationContext ごとではなく ServletContext ごとのシングルトンであり(特定の Web アプリケーションに複数存在する可能性があります)、実際に公開されているため、次のように表示されます。ServletContext 属性。

アノテーション駆動型コンポーネントまたは Java 構成を使用する場合、@ApplicationScope アノテーションを使用して、コンポーネントを application スコープに割り当てることができます。次の例は、その方法を示しています。

  • Java

  • Kotlin

@ApplicationScope
@Component
public class AppPreferences {
	// ...
}
@ApplicationScope
@Component
class AppPreferences {
	// ...
}

WebSocket スコープ

WebSocket スコープは WebSocket セッションのライフサイクルに関連付けられており、WebSocket アプリケーションを介した STOMP に適用されます。詳細については、WebSocket スコープを参照してください。

依存関係としてのスコープ Bean

Spring IoC コンテナーは、オブジェクト(Bean)のインスタンス化だけでなく、コラボレーター(または依存関係)の接続も管理します。(たとえば)HTTP リクエストスコープの Bean を、より寿命の長いスコープの別の Bean に注入する場合、スコープ付き Bean の代わりに AOP プロキシを注入することを選択できます。つまり、スコープオブジェクトと同じパブリックインターフェースを公開するプロキシオブジェクトを挿入する必要がありますが、関連するスコープ(HTTP リクエストなど)から実際のターゲットオブジェクトを取得し、メソッド呼び出しを実際のオブジェクトに委譲することもできます。

singleton をスコープとする Bean 間で <aop:scoped-proxy/> を使用することもできます。その場合、参照はシリアライズ可能な中間プロキシを通過するため、デシリアライズ時にターゲットシングルトン Bean を再取得できます。

<aop:scoped-proxy/> をスコープ prototype の Bean に対して宣言すると、共有プロキシでのすべてのメソッド呼び出しは、呼び出しが転送される新しいターゲットインスタンスの作成につながります。

また、スコーププロキシは、より短いスコープから Bean にライフサイクルセーフな方法でアクセスする唯一の方法ではありません。また、インジェクションポイント (つまり、コンストラクターまたは setter 引数、あるいは自動フィールドです) を ObjectFactory<MyTargetBean> として宣言することで、getObject() 呼び出しが必要になるたびに、インスタンスを保持したり個別に格納したりすることなく、現在のインスタンスをオンデマンドで取得できるようにすることもできます。

拡張バリアントとして、ObjectProvider<MyTargetBean> を宣言して、getIfAvailable や getIfUnique を含むいくつかの追加のアクセスバリアントを提供できます。

これの JSR-330 バリアントは Provider と呼ばれ、検索試行ごとに Provider<MyTargetBean> 宣言および対応する get() 呼び出しとともに使用されます。JSR-330 全体の詳細については、こちらを参照してください。

次の例の構成は 1 行のみですが、その背後にある「理由」と「方法」を理解することが重要です。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop
		https://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- an HTTP Session-scoped bean exposed as a proxy -->
	<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
		<!-- instructs the container to proxy the surrounding bean -->
		<aop:scoped-proxy/> (1)
	</bean>

	<!-- a singleton-scoped bean injected with a proxy to the above bean -->
	<bean id="userService" class="com.something.SimpleUserService">
		<!-- a reference to the proxied userPreferences bean -->
		<property name="userPreferences" ref="userPreferences"/>
	</bean>
</beans>
1 プロキシを定義する行。

このようなプロキシを作成するには、子 <aop:scoped-proxy/> 要素をスコープ付き Bean 定義に挿入します ( 作成するプロキシの型の選択および XML スキーマベースの構成を参照)。

一般的なシナリオでは、requestsession、カスタムスコープレベルでスコープ指定された Bean の定義に <aop:scoped-proxy/> 要素が必要なのはなぜですか ? 次のシングルトン Bean 定義を検討し、前述のスコープに対して定義する必要があるものと対比してください (次の userPreferences Bean 定義は、そのままでは不完全であることに注意してください)。

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

<bean id="userManager" class="com.something.UserManager">
	<property name="userPreferences" ref="userPreferences"/>
</bean>

前の例では、シングルトン Bean(userManager)に HTTP Session -scoped Bean(userPreferences)への参照が挿入されています。ここでの重要な点は、userManager Bean がシングルトンであるということです。コンテナーごとに 1 回だけインスタンス化され、その依存関係(この場合は 1 つだけ、userPreferences Bean)も 1 回だけ注入されます。これは、userManager Bean が、まったく同じ userPreferences オブジェクト(つまり、最初に注入されたオブジェクト)でのみ動作することを意味します。

これは、寿命の短いスコープ Bean を寿命の長いスコープ Bean に注入するときの動作ではありません(たとえば、Bean を依存関係としてシングルトン Bean にコラボレーションする HTTP Session -scoped を注入します)。むしろ、単一の userManager オブジェクトが必要であり、HTTP Session の存続期間中、HTTP Session に固有の userPreferences オブジェクトが必要です。コンテナーは、UserPreferences クラス(理想的には UserPreferences インスタンスであるオブジェクト)とまったく同じパブリックインターフェースを公開するオブジェクトを作成します。オブジェクトは、スコープメカニズム(HTTP リクエスト、Session など)から実際の UserPreferences オブジェクトをフェッチできます。コンテナーは、このプロキシオブジェクトを userManager Bean に注入します。これは、この UserPreferences 参照がプロキシであることを認識していません。この例では、UserManager インスタンスが依存関係が注入された UserPreferences オブジェクトのメソッドを呼び出すとき、実際にはプロキシのメソッドを呼び出しています。次に、プロキシは(この場合)HTTP Session から実際の UserPreferences オブジェクトをフェッチし、取得した実際の UserPreferences オブジェクトにメソッド呼び出しを委譲します。

次の例に示すように、request- Bean および session-scoped Bean をコラボレーションオブジェクトに注入する場合は、次の(正しい完全な)構成が必要です。

<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
	<aop:scoped-proxy/>
</bean>

<bean id="userManager" class="com.something.UserManager">
	<property name="userPreferences" ref="userPreferences"/>
</bean>

作成するプロキシの型の選択

デフォルトでは、Spring コンテナーが <aop:scoped-proxy/> 要素でマークアップされた Bean のプロキシを作成すると、CGLIB ベースのクラスプロキシが作成されます。

CGLIB プロキシはプライベートメソッドをインターセプトしません。このようなプロキシ上でプライベートメソッドを呼び出そうとしても、実際のスコープ付きターゲットオブジェクトには委譲されません。

または、<aop:scoped-proxy/> 要素の proxy-target-class 属性の値に false を指定することにより、Spring コンテナーを設定して、そのようなスコープ Bean の標準 JDK インターフェースベースのプロキシを作成できます。JDK インターフェースベースのプロキシを使用すると、そのようなプロキシに影響を与えるためにアプリケーションクラスパスに追加のライブラリを必要としないことを意味します。ただし、スコープ付き Bean のクラスは少なくとも 1 つのインターフェースを実装する必要があり、スコープ付き Bean が挿入されるすべてのコラボレーターは、そのインターフェースの 1 つを介して Bean を参照する必要があります。次の例は、インターフェースに基づいたプロキシを示しています。

<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session">
	<aop:scoped-proxy proxy-target-class="false"/>
</bean>

<bean id="userManager" class="com.stuff.UserManager">
	<property name="userPreferences" ref="userPreferences"/>
</bean>

クラスベースまたはインターフェースベースのプロキシの選択の詳細については、プロキシメカニズムを参照してください。

リクエスト / セッション参照の直接挿入

ファクトリスコープの代替として、Spring WebApplicationContext は、他の Bean の通常のインジェクションポイントの横にある型ベースのオートワイヤーを通じて、Spring 管理の Bean への HttpServletRequestHttpServletResponseHttpSessionWebRequest と (JSF が存在する場合) FacesContext および ExternalContext の注入もサポートします。Spring は通常、そのようなリクエストおよびセッションオブジェクトにプロキシを挿入します。これには、ファクトリスコープ Bean のスコーププロキシと同様に、シングルトン Bean および直列化可能 Bean でも動作するという利点があります。

カスタムスコープ

Bean スコーピングメカニズムは拡張可能です。独自のスコープを定義することも、既存のスコープを再定義することもできますが、後者は悪い習慣と見なされ、組み込みの singleton および prototype スコープをオーバーライドすることはできません。

カスタムスコープの作成

カスタムスコープを Spring コンテナーに統合するには、このセクションで説明する org.springframework.beans.factory.config.Scope インターフェースを実装する必要があります。独自のスコープを実装する方法のアイデアについては、Spring Framework 自体と Scope javadoc で提供される Scope 実装を参照してください。これにより、実装する必要があるメソッドが詳細に説明されます。

Scope インターフェースには、スコープからオブジェクトを取得し、スコープから削除し、破棄するための 4 つのメソッドがあります。

たとえば、セッションスコープの実装は、セッションスコープの Bean を返します(存在しない場合、メソッドは、Bean の新しいインスタンスを、将来の参照のためにセッションにバインドした後に返します)。次のメソッドは、基になるスコープからオブジェクトを返します。

  • Java

  • Kotlin

Object get(String name, ObjectFactory<?> objectFactory)
fun get(name: String, objectFactory: ObjectFactory<*>): Any

たとえば、セッションスコープの実装は、基になるセッションからセッションスコープの Bean を削除します。オブジェクトが返されますが、指定された名前のオブジェクトが見つからない場合は null を返すことができます。次のメソッドは、基になるスコープからオブジェクトを削除します。

  • Java

  • Kotlin

Object remove(String name)
fun remove(name: String): Any

次のメソッドは、スコープが破棄されたとき、またはスコープ内の指定されたオブジェクトが破棄されたときにスコープが呼び出すコールバックを登録します。

  • Java

  • Kotlin

void registerDestructionCallback(String name, Runnable destructionCallback)
fun registerDestructionCallback(name: String, destructionCallback: Runnable)

破棄コールバックの詳細については、javadoc または Spring スコープの実装を参照してください。

次のメソッドは、基になるスコープの会話識別子を取得します。

  • Java

  • Kotlin

String getConversationId()
fun getConversationId(): String

この識別子はスコープごとに異なります。セッションスコープの実装の場合、この識別子はセッション識別子にすることができます。

カスタムスコープの使用

1 つ以上のカスタム Scope 実装を作成してテストした後、Spring コンテナーに新しいスコープを認識させる必要があります。以下の方法は、新しい Scope を Spring コンテナーに登録する中心的な方法です。

  • Java

  • Kotlin

void registerScope(String scopeName, Scope scope);
fun registerScope(scopeName: String, scope: Scope)

このメソッドは、Spring に同梱されているほとんどの具体的な ApplicationContext 実装の BeanFactory プロパティを介して利用可能な ConfigurableBeanFactory インターフェースで宣言されています。

registerScope(..) メソッドの最初の引数は、スコープに関連付けられた一意の名前です。Spring コンテナー自体のこのような名前の例は、singleton および prototype です。registerScope(..) メソッドの 2 番目の引数は、登録して使用するカスタム Scope 実装の実際のインスタンスです。

カスタム Scope 実装を作成し、次の例に示すように登録するとします。

次の例では、Spring に含まれていますが、デフォルトでは登録されていない SimpleThreadScope を使用します。手順は、独自のカスタム Scope 実装でも同じです。
  • Java

  • Kotlin

Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
val threadScope = SimpleThreadScope()
beanFactory.registerScope("thread", threadScope)

その後、次のように、カスタム Scope のスコープ規則に準拠する Bean 定義を作成できます。

<bean id="..." class="..." scope="thread">

カスタム Scope 実装を使用すると、スコープのプログラムによる登録に限定されません。次の例に示すように、CustomScopeConfigurer クラスを使用して、Scope の登録を宣言的に行うこともできます。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop
		https://www.springframework.org/schema/aop/spring-aop.xsd">

	<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
		<property name="scopes">
			<map>
				<entry key="thread">
					<bean class="org.springframework.context.support.SimpleThreadScope"/>
				</entry>
			</map>
		</property>
	</bean>

	<bean id="thing2" class="x.y.Thing2" scope="thread">
		<property name="name" value="Rick"/>
		<aop:scoped-proxy/>
	</bean>

	<bean id="thing1" class="x.y.Thing1">
		<property name="thing2" ref="thing2"/>
	</bean>

</beans>
FactoryBean 実装の <bean> 宣言内に <aop:scoped-proxy/> を配置すると、スコープが設定されるのはファクトリ Bean 自体であり、getObject() から返されるオブジェクトではありません。