スプリングのビーンスコープ
Spring Beanのスコープを使用することで、Beanインスタンスの生成をより詳細に制御することができます。場合によっては、Beanインスタンスをシングルトンとして作成したいこともありますが、他の場合ではリクエストごとに作成したり、セッション内で一度だけ作成したりすることも考えられます。
スプリングのビーンスコープ
以下には5つの種類のスプリングビーンスコープがあります。
-
- シングルトン – スプリングコンテナでスプリングビーンの唯一のインスタンスが作成されます。これはデフォルトのスプリングビーンスコープです。このスコープを使用する際には、ビーンに共有インスタンス変数がないことを確認してください。そうでないと、データの一貫性の問題が発生する可能性があります。
プロトタイプ – スプリングコンテナからビーンが要求されるたびに、新しいインスタンスが作成されます。
リクエスト – これはプロトタイプスコープと同じですが、ウェブアプリケーションで使用するためのものです。HTTPリクエストごとに新しいビーンのインスタンスが作成されます。
セッション – コンテナによってHTTPセッションごとに新しいビーンが作成されます。
グローバルセッション – ポートレットアプリケーション用のグローバルセッションビーンを作成するために使用されます。
スプリングのビーンには、シングルトンスコープとプロトタイプスコープの2つのオプションがあります。 (Supuringu no biin ni wa, shinguruton sukoopu to purototaipu sukoopu no futatsu no opushon ga arimasu.)
SpringのBeanシングルトンとプロトタイプのスコープは、スタンドアロンのSpringアプリケーションで使用することができます。@Scopeアノテーションを使用して、これらのスコープを簡単に設定する方法を見てみましょう。 JavaのBeanクラスがあるとしましょう。
package com.scdev.spring;
public class MyBean {
public MyBean() {
System.out.println("MyBean instance created");
}
}
「スプリングのコンテナからMyBeanインスタンスを取得するメソッドを定義する、スプリングの設定クラスを定義しましょう。」
package com.scdev.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MyConfiguration {
@Bean
@Scope(value="singleton")
public MyBean myBean() {
return new MyBean();
}
}
シングルトンはデフォルトのスコープですので、上記のビーン定義から @Scope(value=”singleton”) を削除することができます。さて、メインメソッドを作成してシングルトンのスコープをテストしましょう。
package com.scdev.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MySpringApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(MyConfiguration.class);
ctx.refresh();
MyBean mb1 = ctx.getBean(MyBean.class);
System.out.println(mb1.hashCode());
MyBean mb2 = ctx.getBean(MyBean.class);
System.out.println(mb2.hashCode());
ctx.close();
}
}
上記のプログラムが実行されると、以下のような出力が得られます。
MyBean instance created
867988177
867988177
両方のMyBeanインスタンスが同じハッシュコードを持っており、コンストラクタが一度のみ呼び出されていることに注意してください。これは、Springコンテナが常に同じMyBeanインスタンスを返していることを意味しています。さて、スコープをプロトタイプに変更してみましょう。
@Bean
@Scope(value="prototype")
public MyBean myBean() {
return new MyBean();
}
メインメソッドが実行されると、次の出力が得られます。
MyBean instance created
867988177
MyBean instance created
443934570
毎回Springコンテナから要求されるたびにMyBeanのインスタンスが作成されていることは明らかです。さて、今度はスコープをリクエストに変更しましょう。
@Bean
@Scope(value="request")
public MyBean myBean() {
return new MyBean();
}
この場合、次の例外が発生します。
Exception in thread "main" java.lang.IllegalStateException: No Scope registered for scope name 'request'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:347)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:224)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1015)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:339)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1107)
at com.scdev.spring.MySpringApp.main(MySpringApp.java:12)
スタンドアロンアプリケーションでは、リクエスト、セッション、グローバルセッションのスコープが利用できないからです。
スプリングのビーンのリクエストスコープとセッションスコープ
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.scdev.spring</groupId>
<artifactId>Spring-Boot-MVC</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Spring-Boot-MVC</name>
<description>Spring Beans Scope MVC</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>10</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
「春のコンポーネントを作成し、リクエストとセッションのスコープでSpringビーンとしてSpringコンテナに設定しましょう。」
スプリングビーンのリクエストスコープ
package com.scdev.spring;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DataRequestScope {
private String name = "Request Scope";
public DataRequestScope() {
System.out.println("DataRequestScope Constructor Called");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
春のビーンのセッションスコープ
package com.scdev.spring;
import org.springframework.context.annotation.Scope;
import java.time.LocalDateTime;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DataSessionScope {
private String name = "Session Scope";
public DataSessionScope() {
System.out.println("DataSessionScope Constructor Called at "+LocalDateTime.now());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
スプリングコンポーネント
さて、春のコンポーネントを作成し、春を使用して上記のbeanを自動設定しましょう。
package com.scdev.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Customer {
@Autowired
private DataRequestScope dataRequestScope;
@Autowired
private DataSessionScope dataSessionScope;
public DataRequestScope getDataRequestScope() {
return dataRequestScope;
}
public void setDataRequestScope(DataRequestScope dataRequestScope) {
this.dataRequestScope = dataRequestScope;
}
public DataSessionScope getDataSessionScope() {
return dataSessionScope;
}
public void setDataSessionScope(DataSessionScope dataSessionScope) {
this.dataSessionScope = dataSessionScope;
}
}
春のRestコントローラ
最後に、RestControllerクラスを作成し、テストの目的のためにいくつかのAPIエンドポイントを設定しましょう。
package com.scdev.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloData {
@Autowired
private Customer customer;
@RequestMapping("/nameRS")
public String helloRS() {
return customer.getDataRequestScope().getName();
}
@RequestMapping("/nameSSUpdated")
public String helloSSUpdated() {
customer.getDataSessionScope().setName("Session Scope Updated");
return customer.getDataSessionScope().getName();
}
@RequestMapping("/nameSS")
public String helloSS() {
return customer.getDataSessionScope().getName();
}
}
スプリングブートのセッションタイムアウトの設定
最後に、Spring Bootのセッションタイムアウト変数を設定する必要があります。src/main/resources/application.propertiesに以下のプロパティを追加してください。
server.session.cookie.max-age= 1
server.session.timeout= 1
今後、セッションスコープを持つ私たちのSpring Beanは1分で無効になります。SpringBootMvcApplicationクラスをスプリングブートアプリケーションとして実行してください。下記のエンドポイントが構成されることを確認できるはずです。
2018-05-23 17:02:25.830 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameRS]}" onto public java.lang.String com.scdev.spring.HelloData.helloRS()
2018-05-23 17:02:25.831 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameSSUpdated]}" onto public java.lang.String com.scdev.spring.HelloData.helloSSUpdated()
2018-05-23 17:02:25.832 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameSS]}" onto public java.lang.String com.scdev.spring.HelloData.helloSS()
スプリング・ビーンのリクエストスコープのテスト
どんなブラウザでも開いて、URL https://localhost:8080/nameRS にアクセスし、コンソールの出力を確認してください。リクエストごとに「DataRequestScopeのコンストラクタが呼び出されました」というメッセージが表示されるはずです。
春のビーンセッションスコープのテスト
当社のGitHubリポジトリから、Spring Beans Scope Spring Bootプロジェクトをダウンロードすることができます。