Singleton自体は大丈夫だが、Springで使用するには以下のような問題点があった。
- Singletonパターンを具現するコード自体が多く入る。
- 依存関係上、クライアントが具体クラスに依存し、DIP、OCPに違反する。
- 内部属性を変更したり初期化したりすることは難しい。
- private生成者で子供クラスを作るのは難しい。
Spring Container
以前のコード
package hello.hellospring.singleton;
public class SingletonService {
// 1。 static領域にオブジェクトを1つだけ生成しておく。
private static final SingletonService instance = new SingletonService();
// 2。 publicで開いてオブジェクトインスタンスが必要な場合、このstaticメソッドを通じてのみ照会することができる。
public static SingletonService getInstance() {
return instance;
}
// 3。 生成者をprivateと宣言し、外部からnewキーワードを使ってオブジェクト生成をすることを防ぐ。
private SingletonService() {
}
}
package hello.hellospring.singleton;
public class SingletonService {
}
- getInstance()のようなSingletonパターンを具現するためのコードを作成しなくてもよい。
- クライアントが具体クラスに依存せず、
- DIP、OCP、プライベート生成者から自由にSingletonを使用できるようになった。
以前のコード
package hello.hellospring;
public class SingleTonTest {
@Test
void singleTonTest() {
SingletonService singletonService1 = SingletonService.getInstance();
System.out.println(singletonService1);
SingletonService singletonService2 = SingletonService.getInstance();
System.out.println(singletonService2);
Assertions.assertThat(singletonService1).isSameAs(singletonService2);
}
}
package hello.hellospring;
public class SingleTonTest {
@Test
void stateFulTest(){
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
SingletonService singletonService1 = ac.getBean(SingletonService.class);
SingletonService singletonService2 = ac.getBean(SingletonService.class);
Assertions.assertThat(statefulService1).isSameAs(statefulService2);
}
static class TestConfig{
@Bean
public SingletonService singletonService() {
return new SingletonService();
}
}
}
追加)Springの基本Bean登録方式はSingletonであるが、要請するたびに新しいオブジェクトを生成して返却する機能も提供する。 (Bean Scope)
CGLIB
- classのバイトコードを操作するライブラリでSingletonを保障してくれる。
- @Configuationを付けることで、AppConfig classを相続した任意の別のクラス(AppConfig@CGLIB)を作り、それをSpring Beanとして登録する。
- @Beanが付いたメソッドごとにない場合は、生成してSpringBeanに登録して返すコードを作り、
すでにSpring Beanが存在する場合、存在するBeanを返却してSingletonを保障してくれる。
package hello.hellospring;
@Configuration
public class AppConfig {
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
AppConfig@CGLIBはAppConfigの子タイプなので、AppConfig タイプで照会できる。
@Test
void springContainerTest(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);
}