Springのカスタムスコープ

1。概要

箱から出して、Springは任意のSpringアプリケーションで使用できる2つの標準Beanスコープ(「singleton」「prototype」)に加えて、使用するための3つの追加のBeanスコープ(「request」「session」「globalSession」)を提供します。 Web対応アプリケーションでのみ。

標準のBeanスコープをオーバーライドすることはできません。一般に、Web対応スコープをオーバーライドすることは悪い習慣と見なされています。ただし、提供されているスコープにある機能とは異なる機能または追加の機能を必要とするアプリケーションがある場合があります。

たとえば、マルチテナントシステムを開発している場合、テナントごとに特定のBeanまたはBeanのセットの個別のインスタンスを提供したい場合があります。Springは、このようなシナリオのカスタムスコープを作成するためのメカニズムを提供します。

このクイックチュートリアルでは、Springアプリケーションでカスタムスコープを作成、登録、および使用する方法を示します

2.カスタムスコープクラスの作成

カスタムスコープを作成するには、Scopeインターフェイスを実装する必要があります。その際、スコープは複数のBeanファクトリで同時に使用できるため、実装がスレッドセーフであることも確認する必要があります

2.1。スコープオブジェクトとコールバックの管理

カスタムScopeクラスを実装するときに最初に考慮すべきことの1つは、スコープ付きオブジェクトと破棄コールバックをどのように格納および管理するかです。これは、たとえば、マップまたは専用クラスを使用して実行できます。

この記事では、同期マップを使用してスレッドセーフな方法でこれを行います。

カスタムスコープクラスの定義を始めましょう。

public class TenantScope implements Scope { private Map scopedObjects = Collections.synchronizedMap(new HashMap()); private Map destructionCallbacks = Collections.synchronizedMap(new HashMap()); ... }

2.2。スコープからのオブジェクトの取得

スコープから名前でオブジェクトを取得するには、getObjectメソッドを実装しましょう。JavaDocが述べているように、指定されたオブジェクトがスコープに存在しない場合、このメソッドは新しいオブジェクトを作成して返す必要があります

この実装では、名前付きオブジェクトがマップにあるかどうかを確認します。そうである場合はそれを返し、そうでない場合はObjectFactoryを使用して新しいオブジェクトを作成し、それをマップに追加して返します。

@Override public Object get(String name, ObjectFactory objectFactory) { if(!scopedObjects.containsKey(name)) { scopedObjects.put(name, objectFactory.getObject()); } return scopedObjects.get(name); }

Scopeインターフェイスで定義された5つのメソッドのうち、説明されている動作を完全に実装するにはgetメソッドのみが必要です。他の4つのメソッドはオプションであり、機能をサポートする必要がない、またはサポートできない場合、UnsupportedOperationExceptionをスローする可能性があります。

2.3。破壊コールバックの登録

また、registerDestructionCallbackメソッドを実装する必要があります。このメソッドは、指定されたオブジェクトが破棄されたとき、またはスコープ自体がアプリケーションによって破棄されたときに実行されるコールバックを提供します。

@Override public void registerDestructionCallback(String name, Runnable callback) { destructionCallbacks.put(name, callback); }

2.4。スコープからのオブジェクトの削除

次に、removeメソッドを実装しましょう。このメソッドは、名前付きオブジェクトをスコープから削除し、登録された破棄コールバックも削除して、削除されたオブジェクトを返します。

@Override public Object remove(String name) { destructionCallbacks.remove(name); return scopedObjects.remove(name); }

実際にコールバックを実行し、削除されたオブジェクトを破棄するのは呼び出し元の責任であることに注意しください。

2.5。会話IDの取得

それでは、getConversationIdメソッドを実装しましょう。スコープが会話IDの概念をサポートしている場合は、ここに返します。それ以外の場合、規則はnullを返すことです:

@Override public String getConversationId() { return "tenant"; }

2.6。コンテキストオブジェクトの解決

最後に、resolveContextualObjectメソッドを実装しましょう。スコープが複数のコンテキストオブジェクトをサポートしている場合は、それぞれをキー値に関連付け、指定されたキーパラメーターに対応するオブジェクトを返します。それ以外の場合、規則はnullを返すことです:

@Override public Object resolveContextualObject(String key) { return null; }

3.カスタムスコープの登録

Springコンテナに新しいスコープを認識せるには、ConfigurableBeanFactoryインスタンスのregisterScopeメソッドを使用してスコープを登録する必要があります。このメソッドの定義を見てみましょう。

void registerScope(String scopeName, Scope scope);

最初のパラメーターscopeNameは、スコープを一意の名前で識別/指定するために使用されます。2番目のパラメーターscopeは、登録して使用するカスタムScope実装の実際のインスタンスです。

カスタムBeanFactoryPostProcessorを作成し、ConfigurableListableBeanFactoryを使用してカスタムスコープを登録しましょう。

public class TenantBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException { factory.registerScope("tenant", new TenantScope()); } }

それでは、BeanFactoryPostProcessor実装をロードするSpring構成クラスを作成しましょう。

@Configuration public class TenantScopeConfig { @Bean public static BeanFactoryPostProcessor beanFactoryPostProcessor() { return new TenantBeanFactoryPostProcessor(); } }

4.カスタムスコープの使用

カスタムスコープを登録したので@ Scopeアノテーションを使用してカスタムスコープを指定することにより、シングルトン(デフォルトスコープ)以外のスコープを使用する他のBeanと同じように任意のBeanに適用できます。名前で。

単純なTenantBeanクラスを作成しましょう—このタイプのテナントスコープのBeanをすぐに宣言します。

public class TenantBean { private final String name; public TenantBean(String name) { this.name = name; } public void sayHello() { System.out.println( String.format("Hello from %s of type %s", this.name, this.getClass().getName())); } }

私たちはクラスレベルを使用していないことを注意@Component@Scopeこのクラスに注釈を。

それでは、構成クラスでテナントスコープのBeanを定義しましょう。

@Configuration public class TenantBeansConfig { @Scope(scopeName = "tenant") @Bean public TenantBean foo() { return new TenantBean("foo"); } @Scope(scopeName = "tenant") @Bean public TenantBean bar() { return new TenantBean("bar"); } }

5.カスタムスコープのテスト

ApplicationContextをロードし、Configurationクラスを登録し、テナントスコープのBeanを取得することにより、カスタムスコープ構成を実行するためのテストを作成しましょう。

@Test public final void whenRegisterScopeAndBeans_thenContextContainsFooAndBar() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); try{ ctx.register(TenantScopeConfig.class); ctx.register(TenantBeansConfig.class); ctx.refresh(); TenantBean foo = (TenantBean) ctx.getBean("foo", TenantBean.class); foo.sayHello(); TenantBean bar = (TenantBean) ctx.getBean("bar", TenantBean.class); bar.sayHello(); Map foos = ctx.getBeansOfType(TenantBean.class); assertThat(foo, not(equalTo(bar))); assertThat(foos.size(), equalTo(2)); assertTrue(foos.containsValue(foo)); assertTrue(foos.containsValue(bar)); BeanDefinition fooDefinition = ctx.getBeanDefinition("foo"); BeanDefinition barDefinition = ctx.getBeanDefinition("bar"); assertThat(fooDefinition.getScope(), equalTo("tenant")); assertThat(barDefinition.getScope(), equalTo("tenant")); } finally { ctx.close(); } }

そして、私たちのテストからの出力は次のとおりです。

Hello from foo of type org.baeldung.customscope.TenantBean Hello from bar of type org.baeldung.customscope.TenantBean

6.結論

このクイックチュートリアルでは、Springでカスタムスコープを定義、登録、および使用する方法を示しました。

カスタムスコープの詳細については、SpringFrameworkリファレンスを参照してください。また、GitHubのSpringFrameworkリポジトリにあるさまざまなScopeクラスのSpringの実装を確認することもできます。

いつものように、この記事で使用されているコードサンプルはGitHubプロジェクトにあります。