Overview
このクイックチュートリアルでは、SpringフレームワークにおけるさまざまなタイプのBeanのスコープについて学びます。
Beanのスコープは、使用するコンテキストにおけるそのBeanのライフサイクルと可視性を定義します。
Springフレームワークの最新バージョンでは、6種類のスコープが定義されています。
- singleton
- prototype
- request
- session
- application
- websocket
最後に挙げたrequest、session、application、websocketの4つのスコープは、Webを意識したアプリケーションでのみ利用できます。
Further reading:
Spring Beanとは
Spring Beanのアノテーション
シングルトンスコープ
シングルトンスコープでBeanを定義すると、コンテナはそのBeanの単一のインスタンスを作成し、そのBean名に対するすべてのリクエストはキャッシュされた同じオブジェクトを返します。 このオブジェクトに変更が加えられると、そのBeanへのすべての参照に反映されます。 このスコープは、他のスコープが指定されていない場合のデフォルト値です。
スコープの概念を例示するために、Personエンティティを作成してみましょう:
public class Person { private String name; // standard constructor, getters and setters}
その後、@Scopeアノテーションを使用してシングルトンスコープでBeanを定義します:
@Bean@Scope("singleton")public Person personSingleton() { return new Person();}
また、次のようにしてString値の代わりに定数を使用することもできます。
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
さて、同じBeanを参照している2つのオブジェクトが、どちらか一方だけが状態を変更しても、両方とも同じBeanインスタンスを参照しているので、同じ値になることを示すテストを書くことにしましょう:
private static final String NAME = "John Smith";@Testpublic void givenSingletonScope_whenSetName_thenEqualNames() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml"); Person personSingletonA = (Person) applicationContext.getBean("personSingleton"); Person personSingletonB = (Person) applicationContext.getBean("personSingleton"); personSingletonA.setName(NAME); Assert.assertEquals(NAME, personSingletonB.getName()); ((AbstractApplicationContext) applicationContext).close();}
この例のscopes.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 class="org.baeldung.scopes.Person" scope="singleton"/> </beans>
プロトタイプスコープ
プロトタイプスコープを持つBeanは、コンテナから要求されるたびに、異なるインスタンスを返します。
@Bean@Scope("prototype")public Person personPrototype() { return new Person();}
シングルトンスコープで行ったように、定数を使用することもできます
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
さて、プロトタイプスコープで同じBean名を要求する2つのオブジェクトを表示する、前と同様のテストを書きます。 同じBeanインスタンスを参照していないため、これらのオブジェクトは異なる状態になります。
<bean class="org.baeldung.scopes.Person" scope="prototype"/>
Web対応スコープ
前述のとおり、Web対応アプリケーションのコンテキストでのみ利用可能な4つの追加スコープがあります。
リクエスト スコープは、単一の HTTP リクエスト用の Bean インスタンスを作成し、セッション スコープは、HTTP セッション用の Bean インスタンスを作成します。
アプリケーション スコープでは、ServletContext のライフサイクルに合わせて Bean インスタンスを作成し、Websocket スコープでは、特定の WebSocket セッションに合わせて作成します。 リクエストスコープ
@Scopeアノテーションを使用して、リクエストスコープでBeanを定義することができます:
@Bean@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)public HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator();}
proxyMode属性が必要なのは、Webアプリケーションコンテキストのインスタンス化の時点では、アクティブなリクエストが存在しないからです。
また、上記の定義のショートカットとして機能する@RequestScope構成アノテーションを使用することもできます:
@Bean@RequestScopepublic HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator();}
次に、requestScopedBeanへの注入された参照を持つコントローラを定義します。
リクエストが実行されるたびにメッセージを表示すると、後にメソッド内で変更されたにもかかわらず、値が null にリセットされていることがわかります。 これは、リクエストごとに異なる Bean インスタンスが返されているためです。
@Controllerpublic class ScopesController { @Resource(name = "requestScopedBean") HelloMessageGenerator requestScopedBean; @RequestMapping("/scopes/request") public String getRequestScopeMessage(final Model model) { model.addAttribute("previousMessage", requestScopedBean.getMessage()); requestScopedBean.setMessage("Good morning!"); model.addAttribute("currentMessage", requestScopedBean.getMessage()); return "scopesExample"; }}
4.2. セッションスコープ
同様の方法で、セッションスコープを持つBeanを定義することができます:
@Bean@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)public HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator();}
Beanの定義を簡単にするために使用できる専用のコンポジションアノテーションもあります:
@Bean@SessionScopepublic HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator();}
次に、sessionScopedBeanへの参照を持つコントローラを定義します。 ここでも、メッセージ フィールドの値がセッションで同じであることを示すために、2 つのリクエストを実行する必要があります。
このケースでは、最初にリクエストが行われたとき、値 message は null です。
@Controllerpublic class ScopesController { @Resource(name = "sessionScopedBean") HelloMessageGenerator sessionScopedBean; @RequestMapping("/scopes/session") public String getSessionScopeMessage(final Model model) { model.addAttribute("previousMessage", sessionScopedBean.getMessage()); sessionScopedBean.setMessage("Good afternoon!"); model.addAttribute("currentMessage", sessionScopedBean.getMessage()); return "scopesExample"; }}
4.3. アプリケーションスコープ
アプリケーションスコープは、ServletContextのライフサイクルのためにBeanインスタンスを作成します。
これはシングルトンスコープと似ていますが、Beanのスコープに関して非常に重要な違いがあります。
Beanがアプリケーションスコープされている場合、同じServletContextで実行されている複数のサーブレットベースのアプリケーション間でBeanの同じインスタンスが共有されますが、シングルトンスコープされているBeanは、単一のアプリケーションコンテキストにのみスコープされます。
アプリケーションスコープでBeanを作成してみましょう:
@Bean@Scope( value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)public HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator();}
リクエストスコープやセッションスコープと同様に、短いバージョンを使用することができます:
@Bean@ApplicationScopepublic HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator();}
では、このBeanを参照するコントローラを作成してみましょう。
@Controllerpublic class ScopesController { @Resource(name = "applicationScopedBean") HelloMessageGenerator applicationScopedBean; @RequestMapping("/scopes/application") public String getApplicationScopeMessage(final Model model) { model.addAttribute("previousMessage", applicationScopedBean.getMessage()); applicationScopedBean.setMessage("Good afternoon!"); model.addAttribute("currentMessage", applicationScopedBean.getMessage()); return "scopesExample"; }}
この場合、一度applicationScopedBeanで設定された値のメッセージは、同じServletContextで実行されていれば、その後のすべてのリクエスト、セッション、さらにはこのBeanにアクセスする異なるサーブレットアプリケーションでも保持されます。
4.4. WebSocket スコープ
最後に、WebSocket スコープで Bean を作成してみましょう:
@Bean@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)public HelloMessageGenerator websocketScopedBean() { return new HelloMessageGenerator();}
最初にアクセスされたとき、WebSocket スコープの Bean は、WebSocket セッション属性に格納されます。
また、WebSocket セッションのみに限定されたシングルトンの動作を示すとも言えます。
本記事の実装は、GitHubプロジェクトでご覧いただけます。
Learn Springコースを通じて、Spring 5とSpring Boot 2を始めましょう:
>> THE COURSE