- 複数のクライアントが1つの同じオブジェクト インスタンスを共有するため、無状態(stateless)に設計する必要がある。
- 特定のクライアントに依存するフィールドがあってはならない。
- なるべく読むことだけが可能でなければならない。 値を修正してはいけない。
- フィールドの代わりにJavaで共有されない、ローカル変数、パラメータ、Thread Localなどを使用しなければならない。
<問題点>
例えば、以下のように状態を維持するフィールドを作ってしまうと、
stateful Service 1 で100、stateful Service 2 で200を注文しましたが、statefulService1が200を注文したと出てくる。
package hello.hellospring.singletone;
public class StatefulService {
// 状態を保つフィールド
private int price;
public void order(String name, int price){
this.price = price;
}
public int getPrice() {
return price;
}
}
package hello.hellospring;
public class SingleToneTest {
static class TestConfig{
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
@Test
void stateFulTest(){
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
// ThreadA : Aユーザー 100 注文
statefulService1.order("userA",100);
// ThreadA : Bユーザー 200 注文
statefulService2.order("userB", 200);
System.out.println(statefulService1.getPrice());
// 100ではなく200が出力される。
// stateful Service 1、stateful Service 2 が同じインスタンスを使用し、
// priceがstatefulになっているからだ。
// このような場合が発生しないように、無状態(stateless)に設計しなければならない。
}
}
<解決策>
this.price=priceを使わず、すぐにreturn priceするように変えなければならない。
package hello.hellospring.singletone;
public class StatefulService {
public int order(String name, int price){
return price;
}
}
package hello.hellospring;
public class SingleToneTest {
static class TestConfig{
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
@Test
void stateFulTest(){
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
int userAPrice = statefulService1.order("userA",100);
int userBPrice = statefulService2.order("userB", 200);
System.out.println(userAPrice);
// 100
System.out.println(userBPrice);
// 200
}
}