μΈνλ° μ€νλ§ ν΅μ¬μ리 κΈ°λ³ΈνΈμμ λμ¨ μ±κΈν€ 컨ν μ΄λμ λν΄ μ 리νλ €κ³ νλ€.
νκ΅μμ κ³ κΈ κ°μ²΄μ§ν₯ νλ‘κ·Έλλ°μ μκ°νλ©΄μ λ€μ΄λ΄€λ λμμΈ ν¨ν΄ μ€ μ±κΈν€ ν¨ν΄μ κ΄ν λ΄μ©μ΄μλ€.
?
μΉ μ΄ν리μΌμ΄μ μμ μ±κΈν€ ν¨ν΄μ΄ νμν μ΄μ λ π μ¬λ¬ κ³ κ°μ΄ λμμ μμ² π μ νκΈ° λλ¬Έμ΄λ€.
λ§μ½ μ¬λ¬ κ³ κ°μ΄ λμμ μμ²νλ μν©μμ μμ²μ λ°λΌ κ³μ κ°μ²΄λ₯Ό μμ±νκ²λλ©΄ λ©λͺ¨λ¦¬ λλΉκ° μ¬ν΄μ§λ€.
κ·Έλμ ν΄λΉ κ°μ²΄κ° λ± 1κ°λ§ μμ±λκ³ , κ·Έ κ°μ²΄λ₯Ό 곡μ ν μ μλλ‘ μ€κ³ν΄μΌνλ€.
νμ§λ§ μ±κΈν€ ν¨ν΄μ μ¬μ©νκ² λλ©΄ ν¨ν΄μ ꡬννλ μ½λ μμ²΄κ° λ§μ΄λ€μ΄κ°κ² λκ³ ,
μμ‘΄κ΄κ³μ ν΄λΌμ΄μΈνΈκ° ꡬ체 ν΄λμ€μ μμ‘΄(DIP μλ°) νκ²λλ λ±μ λ¬Έμ κ° μλ€.
μ€νλ§ μ»¨ν μ΄λλ μ΄λ¬ν μ±κΈν€ ν¨ν΄μ λ¬Έμ μ μ ν΄κ²°νλ©΄μ, κ°μ²΄κ° 1κ°λ§ μμ±λ μ μλλ‘ κ΄λ¦¬νλ€.
(μ€νλ§ λΉ -> μ±κΈν€μλ‘ κ΄λ¦¬λλ λΉ)
μ±κΈν€ 컨ν μ΄λ
βοΈ μ€νλ§ μ»¨ν μ΄λλ μ±κΈν€ ν¨ν΄μ μ μ©νμ§ μμλ, κ°μ²΄ μΈμ€ν΄μ€λ₯Ό μ±κΈν€μΌλ‘ κ΄λ¦¬νλ€.
β‘οΈ μ±κΈν€ ν¨ν΄μ μν μ§μ λΆν μ½λκ° λ€μ΄κ°μ§ μμλλλ€.
βοΈ μ€νλ§ μ»¨ν μ΄λλ μ±κΈν€ 컨ν μ΄λ μν μ νλ€. μ΄λ κ² μ±κΈν€ κ°μ²΄λ₯Ό μμ±νκ³ κ΄λ¦¬νλ κΈ°λ₯μ μ±κΈν€ λ μ§μ€νΈλ¦¬λΌ νλ€.
// test code
public class SingletonTest {
@Test
@DisplayName("μ€νλ§ μ»¨ν
μ΄λ")
void singletonContainer() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService1 = ac.getBean("memberService",MemberService.class);
MemberService memberService2 = ac.getBean("memberService",MemberService.class);
// μ°Έμ‘°κ°μ΄ κ°μ κ² νμΈ
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
// memberService == memberService1
assertThat(memberService1).isSameAs(memberService2);
}
}
π¨ μ£Όμ
μ±κΈν€ ν¨ν΄μ΄λ , μ±κΈν€ 컨ν μ΄λλ₯Ό μ¬μ©νλ νλλ§ μμ±ν΄μ 곡μ νλ λ°©μμ
νλμ κ°μ κ°μ²΄ μΈμ€ν΄μ€λ₯Ό 곡μ νκΈ° λλ¬Έμ μνλ₯Ό μ μ§(stateful)νκ² μ€κ³νλ©΄ μλλ€ !!!
β‘οΈ λ¬΄μν(stateless)λ‘ μ€κ³ν΄μΌν¨ (˜ νΉμ ν΄λΌμ΄μΈνΈ(μμμμλ order)κ° κ°μ λ³κ²½ν μ μλ νλκ° μμΌλ©΄ μλ¨, κ°κΈμ μ½κΈ°λ§ κ°λ₯νκ²)
π 무μνλ‘ μ€κ³ν΄μΌνλ€λ λ§μ΄ μ’ μ΄λ €μ λλ°, κ°λ¨νκ² '맴λ²λ³μλ₯Ό κ°μ§μ§ μμμΌνλ€' λΌκ³ μκ°νλ©΄λ¨
public class StatefulService {
private int price; // μνλ₯Ό μ μ§νλ νλ
public void order(String name; int price) {
this.price = price; // λ¬Έμ
}
public int getPrice() {
return price;
}
}
// test
class StatefulServiceTest {
@Test
void StatefulServiceSingleton() {
// μ°Έμ‘° κ° λμΌ
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
statefulService1.order("user1", 10000);
statefulService2.order("user2", 20000);
int price = statefulService1.getPrice();
// statefulService1μμμ priceμμΌλ‘ 10000μΌλ‘ μκ°νκ² μ§λ§
// statefulService2μμ priceμ κ°μ 20000μΌλ‘ λ³κ²½ (this.price = price;)
// κ°μ κ°μ²΄λ₯Ό μ°Έμ‘°νκ³ μκΈ° λλ¬Έμ(μ±κΈν€)
System.out.println(price); // 20000 μΆλ ₯
}
}
@Configuration
AppConfigμ½λλ₯Ό 보면
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
...
}
memberService()λ₯Ό νΈμΆνλ©΄ MemoryMemberRepository κ°μ²΄κ° μμ±λκ³ ,
OrderService()λ₯Ό νΈμΆ νμ λλ MemoryMemberRepository μμ±λλ€.
λ§μΉ λ€λ₯Έ MemoryMemberRepository κ°μ²΄κ° μμ±λλ κ²μ²λΌ 보μ΄κΈ° λλ¬Έμ, μ±κΈν€μ΄ κΉ¨μ§ κ²μ²λΌ 보μΈλ€.
κ·Έλ μ§λ§ μ€μ λ‘ νμΈμ μν΄ ν μ€νΈ μ½λλ₯Ό μ§λ©΄ κ°μ μΈμ€ν΄μ€κ° 곡μ λμ΄ μ¬μ©λλ€λ κ²μ μ μ μλ€.
κ·Έ μ΄μ λ, μ€νλ§μ΄ AppConfig ν΄λμ€λ₯Ό μμλ°μ μμμ λ€λ₯Έ ν΄λμ€λ₯Ό λ§λ€κ³ (AppConfig@CGLIB) κ·Έ λ€λ₯Έ ν΄λμ€λ₯Ό μ€νλ§ λΉμΌλ‘ λ±λ‘νκΈ° λλ¬Έμ΄λ€.
κ·Έ μμμ ν΄λμ€κ° μ±κΈν€μ΄ 보μ₯λλλ‘ ν΄μ€λ€(λ°μ΄νΈ μ½λλ₯Ό μ‘°μν΄μ μμ±λ¨)
@Beanμ΄ λΆμ λ©μλ λ§λ€ μ΄λ―Έ μ€νλ§ λΉμ΄ μ‘΄μ¬νλ©΄ μ‘΄μ¬νλ λΉμ λ°ν, μμΌλ©΄ μμ± -> μ±κΈν€ 보μ₯
π @Configurationμ μ¬μ©νμ§ μκ³ @Beanλ§ μ μ©ν κ²½μ° μμμ ν΄λμ€(AppConfig@CGLIB)λ₯Ό μμ±ν΄μ£Όμ§ μκΈ° λλ¬Έμ λ¬Έμ λ°μ
β‘οΈ λ€λ₯Έ κ°μ²΄λ₯Ό μμ±, μ±κΈν€μ΄ κΉ¨μ§
π μ¦, μ€νλ§ μ€μ μ 보λ νμ @Configurationμ μ¬μ©νμ.