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

1概要

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

Beanの有効範囲は、そのBeanが使用されるコンテキストにおけるそのBeanのライフサイクルと可視性を定義します。

Springフレームワークの最新版は6種類のスコープを定義しています。

シングルトン

プロトタイプ

  • リクエスト

  • セッション

  • 応用

  • websocket

前述の最後の4つのスコープ request、session、application および websocket __はWeb対応アプリケーションでのみ利用可能です。

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定義が含まれています。

<?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="personSingleton" class="org.baeldung.scopes.Person" scope="singleton"/>
</beans>

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定義が追加されています。

<bean id="personPrototype" class="org.baeldung.scopes.Person" scope="prototype"/>

4 Web対応の範囲

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

request スコープは単一のHTTPリクエスト用のBeanインスタンスを作成し、s __ession __スコープはHTTPセッション用に作成します。

__application scopeはライフサイクル ServletContext のBeanインスタンスを作成し、 webbsocket scopeは特定の WebSocket __sessionに対して作成します。

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();
}

Webアプリケーションコンテキストのインスタンス化の時点ではアクティブな要求がないため、 proxyMode 属性が必要です。 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つのリクエストを実行する必要があります。

この場合、初めて要求が出されたときには、値 message は____nullです。ただし、一度変更すると、セッション全体で同じ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 内で実行されている複数のサーブレットベースのアプリケーション間で共有されますが、singletonスコープの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にアクセスする別のサーブレットアプリケーションでも保持されます。

4.4. WebSocketスコープ

最後に、 _ webbsocket _ 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スコープとそれらの意図された使用法が何であるかを実証しました。

このチュートリアルの実装はhttps://github.com/eugenp/tutorials/tree/master/spring-all[githubプロジェクト]にあります - これはEclipseベースのプロジェクトなので、インポートと実行は簡単なはずです。そのまま。