Spring Beanスコープのクイックガイド

Spring Beanスコープのクイックガイド

1. 概要

このクイックチュートリアルでは、SpringフレームワークのさまざまなタイプのBeanスコープについて学習します。

Beanのスコープは、使用されるコンテキストでのBeanのライフサイクルと可視性を定義します。

Springフレームワークの最新バージョンでは、6種類のスコープが定義されています。

  • シングルトン

  • プロトタイプ

  • 要求

  • セッション

  • 応用

  • ウェブソケット

request, session, applicationおよびwebsocket に記載されている最後の4つのスコープは、Web対応アプリケーションでのみ使用できます。

参考文献:

Spring Beanとは何ですか?

Spring Beanとは何かについての迅速かつ実用的な説明。

Spring Beanアノテーション

標準のSpring Beanアノテーション(@ Component、@ Repository、@ Service、@ Controller)を使用する方法とタイミングを学びます。

2. シングルトンスコープ

singletonスコープでBeanを定義すると、コンテナーはそのBeanの単一インスタンスを作成し、そのBean名に対するすべての要求は、キャッシュされた同じオブジェクトを返します。 オブジェクトへの変更は、Beanへのすべての参照に反映されます。 他のスコープが指定されていない場合、このスコープはデフォルト値です。

スコープの概念を例示するために、Personエンティティを作成しましょう。

public class Person {
    private String name;

    // standard constructor, getters and setters
}

その後、@Scopeアノテーションを使用して、singletonスコープで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";

@Test
public 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ファイルには、使用されるBeanのxml定義が含まれている必要があります。




    

3. プロトタイプのスコープ

スコープがprototypeのBeanは、コンテナーから要求されるたびに異なるインスタンスを返します。 これは、Bean定義の値prototype@Scopeアノテーションに設定することによって定義されます。

@Bean
@Scope("prototype")
public Person personPrototype() {
    return new Person();
}

また、シングルトンスコープで行ったように定数を使用することもできます。

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

スコーププロトタイプで同じBean名を要求する2つのオブジェクトが、同じBeanインスタンスを参照していないため、状態が異なることを示す、以前と同様のテストを作成します。

private static final String NAME = "John Smith";
private static final String NAME_OTHER = "Anna Jones";

@Test
public void givenPrototypeScope_whenSetNames_thenDifferentNames() {
    ApplicationContext applicationContext =
      new ClassPathXmlApplicationContext("scopes.xml");

    Person personPrototypeA = (Person) applicationContext.getBean("personPrototype");
    Person personPrototypeB = (Person) applicationContext.getBean("personPrototype");

    personPrototypeA.setName(NAME);
    personPrototypeB.setName(NAME_OTHER);

    Assert.assertEquals(NAME, personPrototypeA.getName());
    Assert.assertEquals(NAME_OTHER, personPrototypeB.getName());

    ((AbstractApplicationContext) applicationContext).close();
}

scopes.xmlファイルは、前のセクションで示したものと似ていますが、prototypeスコープを持つBeanのxml定義を追加しています。

4. Web対応スコープ

前述のように、Web対応のアプリケーションコンテキストでのみ使用可能な4つの追加スコープがあります。 これらは実際にはあまり使用されません。

requestスコープは単一のHTTP要求のBeanインスタンスを作成し、sessionスコープはHTTPセッションのBeanインスタンスを作成します。

application scopeは、ライフサイクルServletContextのBeanインスタンスを作成し、websocket scopeは特定のWebSocket セッションのBeanインスタンスを作成します。

Beanをインスタンス化するために使用するクラスを作成しましょう。

public class HelloMessageGenerator {
    private String message;

    // standard getter and setter
}

4.1. リクエスト範囲

@Scopeアノテーションを使用して、requestスコープでBeanを定義できます。

@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloMessageGenerator requestScopedBean() {
    return new HelloMessageGenerator();
}

proxyMode属性が必要なのは、Webアプリケーションコンテキストのインスタンス化の時点では、アクティブな要求がないためです。 Springは、依存関係として挿入されるプロキシを作成し、リクエストで必要になったときにターゲットBeanをインスタンス化します。

次に、requestScopedBeanへの参照が挿入されたコントローラーを定義できます。 Web固有のスコープをテストするには、同じリクエストに2回アクセスする必要があります。

リクエストが実行されるたびにmessageを表示すると、後でメソッドで変更されても、値がnullにリセットされていることがわかります。 これは、リクエストごとに異なるBeanインスタンスが返されるためです。

@Controller
public 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. セッションスコープ

同様の方法で、sessionスコープでBeanを定義できます。

@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloMessageGenerator sessionScopedBean() {
    return new HelloMessageGenerator();
}

次に、sessionScopedBeanを参照してコントローラーを定義します。 ここでも、messageフィールドの値がセッションで同じであることを示すために、2つの要求を実行する必要があります。

この場合、初めてリクエストが行われると、値messagenull. になりますが、一度変更されると、その値 isは、の同じインスタンスとして後続のリクエストのために保持されます。 Beanはセッション全体で返されます。

@Controller
public 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. 適用範囲

application scopeは、ServletContext.のライフサイクルのBeanインスタンスを作成します

これはシングルトンスコープに似ていますが、Beanのスコープに関して非常に重要な違いがあります。

Beanがapplicationスコープの場合、Beanの同じインスタンスが同じServletContextで実行されている複数のサーブレットベースのアプリケーション間で共有されますが、シングルトンスコープのBeanは単一のアプリケーションコンテキストのみにスコープされます。

applicationスコープでBeanを作成しましょう:

@Bean
@Scope(
  value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloMessageGenerator applicationScopedBean() {
    return new HelloMessageGenerator();
}

そして、このBeanを参照するコントローラー:

@Controller
public 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 に一度設定された値messageは、同じServletContext.で実行されていれば、後続のすべてのリクエスト、セッション、およびこのBeanにアクセスする別のサーブレットアプリケーションに対しても保持されます。 )s

4.4. WebSocketスコープ

最後に、websocket scopeを使用してBeanを作成しましょう。

@Bean
@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloMessageGenerator websocketScopedBean() {
    return new HelloMessageGenerator();
}

最初にアクセスされたときのWebSocketスコープのBeanは、WebSocketセッション属性に格納されます。 その後、WebSocketセッション全体でそのBeanにアクセスするたびに、Beanの同じインスタンスが返されます。

シングルトン動作を示しますが、WebSocketセッションのみに制限されているとも言えます。

5. 結論

Springが提供するさまざまなBeanスコープと、それらの意図された使用法を示しました。

このチュートリアルの実装はthe GitHub projectにあります。これはEclipseベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。