[Spring] 9.빈 μŠ€μ½”ν”„

 

1. 빈 μŠ€μ½”ν”„λž€?

- μŠ€ν”„λ§ 빈이 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ˜ μ‹œμž‘κ³Ό ν•¨κ»˜ μƒμ„±λ˜μ–΄ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ μ’…λ£Œλ  λ•Œ κΉŒμ§€ μœ μ§€ 

  -> μŠ€ν”„λ§ 빈이 기본적으둜 싱글톀 μŠ€μ½”ν”„λ‘œ μƒμ„±λ˜κΈ° λ•Œλ¬Έ

  -> 빈 μŠ€μ½”ν”„ : 빈이 μ‘΄μž¬ν•  수 μžˆλŠ” λ²”μœ„

 

μŠ€ν”„λ§μ€ λ‹€μŒκ³Ό 같은 λ‹€μ–‘ν•œ μŠ€μ½”ν”„λ₯Ό 지원

- 싱글톀 : κΈ°λ³Έ μŠ€μ½”νΌ, μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ˜ μ‹œμž‘κ³Ό μ’…λ£ŒκΉŒμ§€ μœ μ§€λ˜λŠ” κ°€μž₯ 넓은 λ²”μœ„μ˜ μŠ€μ½”ν”„

- ν”„λ‘œν† νƒ€μž… : μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” ν”„λ‘œν†  νƒ€μž… 빈의 생성과 μ˜μ‘΄κ΄€κ³„ μ£Όμž…κΉŒμ§€λ§Œ κ΄€μ—¬ / λ”λŠ” κ΄€λ¦¬ν•˜μ§€ μ•ŠλŠ” 맀우 짧은 μŠ€μ½”ν”„

- μ›Ή κ΄€λ ¨ μŠ€μ½”ν”„ 

   > "request"      : μ›Ή μš”μ²­μ΄ λ“€μ–΄μ˜€κ³  λ‚˜κ°ˆ λ•Œ κΉŒμ§€ μœ μ§€λ˜λŠ” μŠ€μ½”ν”„

   > "session"       : μ›Ή μ„Έμ…˜μ΄ μƒμ„±λ˜κ³  μ’…λ£Œλ  λ•Œ κΉŒμ§€ μœ μ§€λ˜λŠ” μŠ€μ½”ν”„ 

   > "application" :  μ›Ήμ˜ μ„œλΈ”λ¦Ώ μ»¨ν…μŠ€μ™€ 같은 λ²”μœ„λ‘œ μœ μ§€λ˜λŠ” μŠ€μ½”ν”„

 

빈 μŠ€μ½”ν”„ 지정 방법

[μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ” μžλ™ 등둝]

@Scope("prototype")
@Component
public class HelloBean {}

[μˆ˜λ™ 등둝]

@Scope("prototype")
@Bean
PrototypeBean HelloBean() {
  return new HelloBean();
}

2. ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„

ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„

- 싱글톀 μŠ€μ½”ν”„μ˜ λΉˆμ„ μ‘°νšŒν•˜λ©΄ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 항상 같은 μΈμŠ€ν„΄μŠ€μ˜ μŠ€ν”„λ§ λΉˆμ„ λ°˜ν™˜

- ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„λ₯Ό μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— μ‘°νšŒν•˜λ©΄ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 항상 μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€ μƒμ„±ν•΄μ„œ λ°˜ν™˜

 

싱글톀 빈 μš”μ²­

- 싱글톀 μŠ€μ½”ν”„μ˜ λΉˆμ„ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— μš”μ²­ν•œλ‹€

- μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 본인이 κ΄€λ¦¬ν•˜λŠ” μŠ€ν”„λ§ λΉˆμ„ λ°˜ν™˜ν•œλ‹€

- 이후에 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— 같은 μš”μ²­μ΄ 와도 같은 객체 μΈμŠ€ν„΄μŠ€μ˜ μŠ€ν”„λ§ 빈으λ₯΄ λ°˜ν™˜ν•œλ‹€

 

ν”„λ‘œν† νƒ€μž… 빈 μš”μ²­1

- ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„μ˜ λΉˆμ„ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— μš”μ²­ν•œλ‹€

- μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 이 μ‹œμ μ— ν”„λ‘œν† νƒ€μž… λΉˆμ„ μƒμ„±ν•˜κ³  ν•„μš”ν•œ μ˜μ‘΄κ΄€κ³„λ₯Ό μ£Όμž…ν•œλ‹€

 

ν”„λ‘œν† νƒ€μž… 빈 μš”μ²­2

- μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” μƒμ„±ν•œ ν”„λ‘œν† νƒ€μž… λΉˆμ„ ν΄λΌμ΄μ–ΈνŠΈμ— λ°˜ν™˜

- 이후 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— 같은 μš”μ²­μ΄ 였면 항상 μƒˆλ‘œμš΄ ν”„λ‘œν† νƒ€μž… λΉˆμ„ μƒμ„±ν•΄μ„œ λ°˜ν™˜

 

정리

- μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” ν”„λ‘œν† νƒ€μž… λΉˆμ„ μƒμ„±ν•˜κ³ , μ˜μ‘΄κ΄€κ³„ μ£Όμž…, μ΄ˆκΈ°ν™”κΉŒμ§€λ§Œ 처리

- ν΄λΌμ΄μ–ΈνŠΈμ— λΉˆμ„ λ°˜ν™˜ν•˜κ³  이후 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” μƒμ„±λœ ν”„λ‘œν† νƒ€μž… λΉˆμ„ κ΄€λ¦¬ν•˜μ§€ μ•ŠμŒ

- ν”„λ‘œν† νƒ€μž… λΉˆμ„ 관리할 μ±…μž„μ€ ν”„λ‘œν† νƒ€μž… λΉˆμ„ 받은 ν΄λΌμ΄μ–ΈνŠΈμ— μžˆλ‹€

- κ·Έλž˜μ„œ @PreDestroy같은 μ’…λ£Œ λ©”μ„œλ“œκ°€ ν˜ΈμΆœλ˜μ§€ μ•ŠμŒ

 

싱글톀 μŠ€μ½”ν”„ 빈 ν…ŒμŠ€νŠΈ

public class SingletonTest {
  @Test
  public void singletonBeanFind() {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SingletonBean.class);
    SingletonBean singletonBean1 = ac.getBean(SingletonBean.class);
    SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
    System.out.println("singletonBean1 = " + singletonBean1);
    System.out.println("singletonBean2 = " + singletonBean2);
    assertThat(singletonBean1).isSameAs(singletonBean2);
    ac.close(); //μ’…λ£Œ 
  }

  @Scope("singleton")
  static class SingletonBean {
    @PostConstruct
    public void init() {
      System.out.println("SingletonBean.init");
    }
  
    @PreDestroy
    public void destroy() {
      System.out.println("SingletonBean.destroy");
    }
  } 
}

ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„ 빈 ν…ŒμŠ€νŠΈ

public class PrototypeTest {
  @Test
  public void prototypeBeanFind() {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
    System.out.println("find prototypeBean1");
    PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class); System.out.println("find prototypeBean2");
    PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class); System.out.println("prototypeBean1 = " + prototypeBean1); 
    System.out.println("prototypeBean2 = " + prototypeBean2); 
    assertThat(prototypeBean1).isNotSameAs(prototypeBean2); 
    ac.close(); //μ’…λ£Œ
  }
  
  @Scope("prototype")
  static class PrototypeBean {
    @PostConstruct
    public void init() {
      System.out.println("PrototypeBean.init");
    }
    
    @PreDestroy
    public void destroy() {
      System.out.println("PrototypeBean.destroy");
    }
  } 
}

 

* 각 λΉˆμ— @Component 없어도 AnnotaionConfigApplication 지정해주면 ν•΄λ‹Ή ν΄λž˜μŠ€κ°€ μžλ™μœΌλ‘œ μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ”μ²˜λŸΌ λ™μž‘

 

- 싱글톀 λΉˆμ€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆ 생성 μ‹œμ μ— μ΄ˆκΈ°ν™” λ©”μ„œλ“œκ°€ μ‹€ν–‰ λ˜μ§€λ§Œ, ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„μ˜ λΉˆμ€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ—μ„œ λΉˆμ„ μ‘°νšŒν•  λ•Œ μƒμ„±λ˜κ³ , μ΄ˆκΈ°ν™” λ©”μ„œλ“œλ„ μ‹€ν–‰λœλ‹€.
- ν”„λ‘œν† νƒ€μž… λΉˆμ„ 2번 μ‘°νšŒν–ˆμœΌλ―€λ‘œ μ™„μ „νžˆ λ‹€λ₯Έ μŠ€ν”„λ§ 빈이 μƒμ„±λ˜κ³ , μ΄ˆκΈ°ν™”λ„ 2번 μ‹€ν–‰λœ 것을 확인할 수 μžˆλ‹€.

- 싱글톀 λΉˆμ€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ κ΄€λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ μ’…λ£Œλ  λ•Œ 빈의 μ’…λ£Œ λ©”μ„œλ“œκ°€ μ‹€ν–‰λ˜μ§€λ§Œ, ν”„λ‘œν† νƒ€μž… λΉˆμ€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ 생성과 μ˜μ‘΄κ΄€κ³„ μ£Όμž… 그리고 μ΄ˆκΈ°ν™” κΉŒμ§€λ§Œ κ΄€μ—¬ν•˜κ³ , λ”λŠ” κ΄€λ¦¬ν•˜μ§€ μ•ŠλŠ”λ‹€.
- λ”°λΌμ„œ ν”„λ‘œν† νƒ€μž… λΉˆμ€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ μ’…λ£Œλ  λ•Œ @PreDestroy 같은 μ’…λ£Œ λ©”μ„œλ“œκ°€ μ „ν˜€ μ‹€ν–‰λ˜μ§€ μ•ŠλŠ”λ‹€.

 

ν”„λ‘œν† νƒ€μž… 빈의 νŠΉμ§• 정리

- μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— μš”μ²­ν•  λ•Œ λ§ˆλ‹€ μƒˆλ‘œ μƒμ„±λœλ‹€.
- μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” ν”„λ‘œν† νƒ€μž… 빈의 생성과 μ˜μ‘΄κ΄€κ³„ μ£Όμž… 그리고 μ΄ˆκΈ°ν™”κΉŒμ§€λ§Œ κ΄€μ—¬ν•œλ‹€.
- μ’…λ£Œ λ©”μ„œλ“œκ°€ ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ”λ‹€.
- κ·Έλž˜μ„œ ν”„λ‘œν† νƒ€μž… λΉˆμ€ ν”„λ‘œν† νƒ€μž… λΉˆμ„ μ‘°νšŒν•œ ν΄λΌμ΄μ–ΈνŠΈκ°€ 관리해야 ν•œλ‹€.
- μ’…λ£Œ λ©”μ„œλ“œμ— λŒ€ν•œ ν˜ΈμΆœλ„
ν΄λΌμ΄μ–ΈνŠΈκ°€ 직접 ν•΄μ•Όν•œλ‹€.

3. ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„ - 싱글톀 빈과 ν•¨κ»˜ μ‚¬μš©μ‹œ 문제점

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„μ˜ λΉˆμ„ μš”μ²­ν•˜λ©΄ 항상 μƒˆλ‘œμš΄ 객체 μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•΄μ„œ λ°˜ν™˜

ν•˜μ§€λ§Œ 싱글톀 빈과 ν•¨κ»˜ μ‚¬μš©ν•  땐 μ˜λ„ν•œλŒ€λ‘œ 잘 λ™μž‘ν•˜μ§€ μ•ŠμŒ

 

[μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— ν”„λ‘œν† νƒ€μž… λΉˆμ„ 직접 μš”μ²­ μ˜ˆμ‹œ]

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— ν”„λ‘œν† νƒ€μž… 빈 직접 μš”μ²­1

- ν΄λΌμ΄μ–ΈνŠΈ AλŠ” μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— ν”„λ‘œν† νƒ€μž… λΉˆμ„ μš”μ²­

- μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” ν”„λ‘œν† νƒ€μž… λΉˆμ„ μƒˆλ‘œ μƒμ„±ν•΄μ„œ λ°˜ν™˜(x01) = ν•΄λ‹Ή 빈의 countν•„λ“œ 값은 0

- ν΄λΌμ΄μ–ΈνŠΈλŠ” μ‘°νšŒν•œ ν”„λ‘œν† νƒ€μž… λΉˆμ— addCount()λ₯Ό ν˜ΈμΆœν•˜λ©΄μ„œ count ν•„λ“œ +1

- 결과적으둜 ν”„λ‘œν† νƒ€μž… 빈(x01)의 countλŠ” 1이 됨

 

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— ν”„λ‘œν† νƒ€μž… 빈 직접 μš”μ²­2

- ν΄λΌμ΄μ–ΈνŠΈBλŠ” μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— ν”„λ‘œν† νƒ€μž… λΉˆμ„ μš”μ²­

- μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” ν”„λ‘œν† νƒ€μž… λΉˆμ„ μƒˆλ‘œ μƒμ„±ν•΄μ„œ λ°˜ν™˜(x02) ν•΄λ‹Ή 빈의 count ν•„λ“œ 값은 0

- ν΄λΌμ΄μ–ΈνŠΈλŠ” μ‘°νšŒν•œ ν”„λ‘œν† νƒ€μž… λΉˆμ— addCount()λ₯Ό ν˜ΈμΆœν•˜λ©΄μ„œ countν•„λ“œ +1

- 결과적으둜 ν”„λ‘œν† νƒ€μž… 빈(x02)의 countλŠ” 1이 됨

 

μ½”λ“œ

public class SingletonWithPrototypeTest1 {
  @Test
  void prototypeFind() {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
    PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
    prototypeBean1.addCount();
    assertThat(prototypeBean1.getCount()).isEqualTo(1);
    PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);
    prototypeBean2.addCount();
    assertThat(prototypeBean2.getCount()).isEqualTo(1);
  }
  
  @Scope("prototype")
  static class PrototypeBean {
    private int count = 0;
    
    public void addCount() {
      count++;
    }
    
    public int getCount() {
      return count;
    }
  
    @PostConstruct
    public void init() {
      System.out.println("PrototypeBean.init " + this);
    }
    
    @PreDestroy
    public void destroy() {
      System.out.println("PrototypeBean.destroy");
    }
  } 
}

 

싱글톀 λΉˆμ—μ„œ ν”„λ‘œν† νƒ€μž… 빈 μ‚¬μš©

- clientBeanμ΄λΌλŠ” 싱글톀 빈이 μ˜μ‘΄κ΄€κ³„ μ£Όμž…μ„ ν†΅ν•΄μ„œ ν”„λ‘œν† νƒ€μž… λΉˆμ„ μ£Όμž…λ°›μ•„μ„œ μ‚¬μš©ν•˜λŠ” 예

 

μ‹±κΈ€ν†€μ—μ„œ ν”„λ‘œν† νƒ€μž… 빈 μ‚¬μš©1

- clientBean은 μ‹±κΈ€ν†€μ΄λ―€λ‘œ 보톡 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆ 생성 μ‹œμ μ— ν•¨κ»˜ μƒμ„±λ˜κ³ , μ˜μ‘΄κ΄€κ³„ μ£Όμž…λ„ λ°œμƒ

- clientBean은 μ˜μ‘΄κ΄€κ³„ μžλ™ μ£Όμž… μ‚¬μš© / μ£Όμž… μ‹œμ μ— μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— ν”„λ‘œν† νƒ€μž… λΉˆμ„ μš”μ²­

- μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” ν”„λ‘œν† νƒ€μž… λΉˆμ„ μƒμ„±ν•΄μ„œ clientBean에 λ°˜ν™˜ / ν”„λ‘œν† νƒ€μž… 빈의 countν•„λ“œ 값은 0

- clientBean은 ν”„λ‘œν† νƒ€μž… λΉˆμ„ λ‚΄λΆ€ ν•„λ“œμ— 보관 (μ •ν™•νžˆλŠ” μ°Έμ‘°κ°’ 보관)

 

μ‹±κΈ€ν†€μ—μ„œ ν”„λ‘œν† νƒ€μž… 빈 μ‚¬μš©2

- ν΄λΌμ΄μ–ΈνŠΈAλŠ” clientBean을 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— μš”μ²­ν•΄μ„œ λ°›μŒ / μ‹±κΈ€ν†€μ΄λ―€λ‘œ 항상 같은 clientBean이 λ°˜ν™˜λ¨

- ν΄λΌμ΄μ–ΈνŠΈAλŠ” clientBean.logic()을 호좜

- clientBean은 prototypeBean의 addCount()λ₯Ό ν˜ΈμΆœν•΄μ„œ ν”„λ‘œν† νƒ€μž… 빈의 countλ₯Ό 증가 / count 값이 1이 됨

 

ν…ŒμŠ€νŠΈμ½”λ“œ

public class SingletonWithPrototypeTest1 {
  @Test
  void singletonClientUsePrototype() {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);
    ClientBean clientBean1 = ac.getBean(ClientBean.class);
    int count1 = clientBean1.logic();
    assertThat(count1).isEqualTo(1);
    ClientBean clientBean2 = ac.getBean(ClientBean.class);
    int count2 = clientBean2.logic();
    assertThat(count2).isEqualTo(2);
  }

  static class ClientBean {
    private final PrototypeBean prototypeBean;
    
    @Autowired
    public ClientBean(PrototypeBean prototypeBean) {
      this.prototypeBean = prototypeBean;
    }
    
    public int logic() {
      prototypeBean.addCount();
      int count = prototypeBean.getCount();
      return count;
    } 
  }
  
  @Scope("prototype")
  static class PrototypeBean {
    private int count = 0;
    
    public void addCount() {
      count++;
    }
    
    public int getCount() {
      return count;
    }
  
    @PostConstruct
    public void init() {
      System.out.println("PrototypeBean.init " + this);
    }
  
    @PreDestroy
    public void destroy() {
    System.out.println("PrototypeBean.destroy");
    }
  } 
}

* count 2κ°€ 됨 -> ν”„λ‘œν† νƒ€μž… λΉˆμ€ μƒˆλ‘­κ²Œ λ§Œλ“€μ–΄μ§€λŠ” 것 μ•„λ‹Œκ°€μš”? -> ν΄λΌμ΄μ–ΈνŠΈ 빈 μƒμ„±μ‹œμ μ— 이미 ν”„λ‘œν† νƒ€μž…μ΄ μ£Όμž…λ¨


4. ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„ - 싱글톀 빈과 ν•¨κ»˜ μ‚¬μš©μ‹œ Provider둜 문제 ν•΄κ²°

싱글톀 빈과 ν”„λ‘œν† νƒ€μž… λΉˆμ„ ν•¨κ»˜ μ‚¬μš©ν•  λ•Œ μ–΄λ–»κ²Œν•˜λ©΄ μƒˆλ‘œμš΄ ν”„ν† ν† νƒ€μž… λΉˆμ„ 생성할 수 μžˆμ„κΉŒ?

 

[μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— μš”μ²­]

public class PrototypeProviderTest {
  @Test
  void providerTest() {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);
    ClientBean clientBean1 = ac.getBean(ClientBean.class);
    int count1 = clientBean1.logic();
    assertThat(count1).isEqualTo(1);
    ClientBean clientBean2 = ac.getBean(ClientBean.class);
    int count2 = clientBean2.logic();
    assertThat(count2).isEqualTo(1);
  }
      
  static class ClientBean {
    @Autowired
    private ApplicationContext ac;
    public int logic() {
      PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
      prototypeBean.addCount();
      int count = prototypeBean.getCount();
      return count;
    } 
  }
 
  @Scope("prototype")
  static class PrototypeBean {
    private int count = 0;
    public void addCount() {
      count++;
    }
    
    public int getCount() {
      return count;
    }
    
    @PostConstruct
    public void init() {
      System.out.println("PrototypeBean.init " + this);
    }
          
    @PreDestroy
    public void destroy() {
      System.out.println("PrototypeBean.destroy");
    }
  } 
}
- 싀행해보면 ac.getBean() 을 ν†΅ν•΄μ„œ 항상 μƒˆλ‘œμš΄ ν”„λ‘œν† νƒ€μž… 빈이 μƒμ„±λ˜λŠ” 것을 확인할 수 μžˆλ‹€.
- μ˜μ‘΄κ΄€κ³„λ₯Ό μ™ΈλΆ€μ—μ„œ μ£Όμž…(DI) λ°›λŠ”κ²Œ μ•„λ‹ˆλΌ μ΄λ ‡κ²Œ 직접 ν•„μš”ν•œ μ˜μ‘΄κ΄€κ³„λ₯Ό μ°ΎλŠ” 것을 Dependency Lookup (DL) μ˜μ‘΄κ΄€κ³„ 쑰회(탐색) μ΄λΌν•œλ‹€.
- 그런데 μ΄λ ‡κ²Œ μŠ€ν”„λ§μ˜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ»¨ν…μŠ€νŠΈ 전체λ₯Ό μ£Όμž…λ°›κ²Œ 되면, μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— 쒅속적인 μ½”λ“œκ°€ 되고, λ‹¨μœ„ ν…ŒμŠ€νŠΈλ„ μ–΄λ €μ›Œμ§„λ‹€.

- μ§€κΈˆ ν•„μš”ν•œ κΈ°λŠ₯은 μ§€μ •ν•œ ν”„λ‘œν† νƒ€μž… λΉˆμ„ μ»¨ν…Œμ΄λ„ˆμ—μ„œ λŒ€μ‹  μ°Ύμ•„μ£ΌλŠ” λ”±! DL μ •λ„μ˜ κΈ°λŠ₯만 μ œκ³΅ν•˜λŠ” 무언가가 있으면 λœλ‹€.

 

ObjectFactory, ObjectProvider

- μ§€μ •ν•œ λΉˆμ„ μ»¨ν…Œμ΄λ„ˆμ—μ„œ λŒ€μ‹  μ°Ύμ•„μ£ΌλŠ” DL μ„œλΉ„μŠ€λ₯Ό μ œκ³΅ν•˜λŠ” 것이 λ°”λ‘œ ObjectProvider 이닀.

- 참고둜 κ³Όκ±°μ—λŠ” ObjectFactory κ°€ μžˆμ—ˆλŠ”λ°, 여기에 편의 κΈ°λŠ₯을 μΆ”κ°€ν•΄μ„œ ObjectProvider κ°€ λ§Œλ“€μ–΄μ‘Œλ‹€.

@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
  PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
  prototypeBean.addCount();
  int count = prototypeBean.getCount();
  return count;
}
- 싀행해보면 prototypeBeanProvider.getObject() 을 ν†΅ν•΄μ„œ 항상 μƒˆλ‘œμš΄ ν”„λ‘œν† νƒ€μž… 빈이 μƒμ„±λ˜λŠ” 것을 확인할 수 μžˆλ‹€.
- ObjectProvider 의 getObject() λ₯Ό ν˜ΈμΆœν•˜λ©΄ λ‚΄λΆ€μ—μ„œλŠ” μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλ₯Ό 톡해 ν•΄λ‹Ή λΉˆμ„ μ°Ύμ•„μ„œ λ°˜ν™˜ν•œλ‹€. (DL)
- μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” κΈ°λŠ₯을 μ‚¬μš©ν•˜μ§€λ§Œ, κΈ°λŠ₯이 λ‹¨μˆœν•˜λ―€λ‘œ λ‹¨μœ„ν…ŒμŠ€νŠΈλ₯Ό λ§Œλ“€κ±°λ‚˜ mock μ½”λ“œλ₯Ό λ§Œλ“€κΈ°λŠ” 훨씬 μ‰¬μ›Œμ§„λ‹€.

- ObjectProvider λŠ” μ§€κΈˆ λ”± ν•„μš”ν•œ DL μ •λ„μ˜ κΈ°λŠ₯만 μ œκ³΅ν•œλ‹€.

νŠΉμ§•

- ObjectFactory: κΈ°λŠ₯이 λ‹¨μˆœ, λ³„λ„μ˜ 라이브러리 ν•„μš” μ—†μŒ, μŠ€ν”„λ§μ— 의쑴
- ObjectProvider: ObjectFactory 상속, μ˜΅μ…˜, 슀트림 μ²˜λ¦¬λ“± 편의 κΈ°λŠ₯이 많고, λ³„λ„μ˜ 라이브러리 ν•„μš” μ—†μŒ, μŠ€ν”„λ§μ— 의쑴

JSR-330 Provider

- javax.inject.Provider λΌλŠ” JSR-330 μžλ°” ν‘œμ€€

- gradle에 라이브러리 μΆ”κ°€

μŠ€ν”„λ§λΆ€νŠΈ 3.0 미만
javax.inject:javax.inject:1 라이브러리λ₯Ό gradle dependencies에 μΆ”κ°€ν•΄μ•Ό ν•œλ‹€.

μŠ€ν”„λ§λΆ€νŠΈ 3.0 이상

jakarta.inject:jakarta.inject-api:2.0.1 라이브러리λ₯Ό gradle dependencies에 μΆ”κ°€ν•΄μ•Ό ν•œλ‹€.
dependencies{
  implementation : 'javax.inject:javax.inject:1'
  ...
}
@Autowired
private Provider<PrototypeBean> provider;
public int logic() {
  PrototypeBean prototypeBean = provider.get();
  prototypeBean.addCount();
  int count = prototypeBean.getCount();
  return count;
}
- 싀행해보면 provider.get() 을 ν†΅ν•΄μ„œ 항상 μƒˆλ‘œμš΄ ν”„λ‘œν† νƒ€μž… 빈이 μƒμ„±λ˜λŠ” 것을 확인할 수 μžˆλ‹€.
- provider 의 get() 을 ν˜ΈμΆœν•˜λ©΄ λ‚΄λΆ€μ—μ„œλŠ” μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλ₯Ό 톡해 ν•΄λ‹Ή λΉˆμ„ μ°Ύμ•„μ„œ λ°˜ν™˜ν•œλ‹€. (DL)

- μžλ°” ν‘œμ€€μ΄κ³ , κΈ°λŠ₯이 λ‹¨μˆœν•˜λ―€λ‘œ λ‹¨μœ„ν…ŒμŠ€νŠΈλ₯Ό λ§Œλ“€κ±°λ‚˜ mock μ½”λ“œλ₯Ό λ§Œλ“€κΈ°λŠ” 훨씬 μ‰¬μ›Œμ§„λ‹€.
- Provider λŠ” μ§€κΈˆ λ”± ν•„μš”ν•œ DL μ •λ„μ˜ κΈ°λŠ₯만 μ œκ³΅ν•œλ‹€.

νŠΉμ§•

- get() λ©”μ„œλ“œ ν•˜λ‚˜λ‘œ κΈ°λŠ₯이 맀우 λ‹¨μˆœν•˜λ‹€.
- λ³„λ„μ˜ λΌμ΄λΈŒλŸ¬λ¦¬κ°€ ν•„μš”ν•˜λ‹€.
- μžλ°” ν‘œμ€€μ΄λ―€λ‘œ μŠ€ν”„λ§μ΄ μ•„λ‹Œ λ‹€λ₯Έ μ»¨ν…Œμ΄λ„ˆμ—μ„œλ„ μ‚¬μš©ν•  수 μžˆλ‹€.

μ°Έκ³ :

μ‹€λ¬΄μ—μ„œ μžλ°” ν‘œμ€€μΈ JSR-330 Providerλ₯Ό μ‚¬μš©ν•  것인지, μ•„λ‹ˆλ©΄ μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” ObjectProviderλ₯Ό μ‚¬μš©ν•  것인지 고민이 될 것이닀. ObjectProviderλŠ” DL을 μœ„ν•œ 편의 κΈ°λŠ₯을 많이 μ œκ³΅ν•΄μ£Όκ³  μŠ€ν”„λ§ 외에 λ³„λ„μ˜ μ˜μ‘΄κ΄€κ³„ μΆ”κ°€κ°€ ν•„μš” μ—†κΈ° λ•Œλ¬Έμ— νŽΈλ¦¬ν•˜λ‹€. λ§Œμ•½(정말 κ·ΈλŸ΄μΌμ€ 거의 μ—†κ² μ§€λ§Œ) μ½”λ“œλ₯Ό μŠ€ν”„λ§μ΄ μ•„λ‹Œ λ‹€λ₯Έ μ»¨ν…Œμ΄λ„ˆμ—μ„œλ„ μ‚¬μš©ν•  수 μžˆμ–΄μ•Ό ν•œλ‹€λ©΄ JSR-330 Providerλ₯Ό μ‚¬μš©ν•΄μ•Όν•œλ‹€.

 

μŠ€ν”„λ§μ„ μ‚¬μš©ν•˜λ‹€ 보면 이 κΈ°λŠ₯ 뿐만 μ•„λ‹ˆλΌ λ‹€λ₯Έ κΈ°λŠ₯듀도 μžλ°” ν‘œμ€€κ³Ό μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” κΈ°λŠ₯이

κ²ΉμΉ λ•Œκ°€ 많이 μžˆλ‹€. λŒ€λΆ€λΆ„ μŠ€ν”„λ§μ΄ 더 λ‹€μ–‘ν•˜κ³  νŽΈλ¦¬ν•œ κΈ°λŠ₯을 μ œκ³΅ν•΄μ£ΌκΈ° λ•Œλ¬Έμ—, νŠΉλ³„νžˆ λ‹€λ₯Έ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ‚¬μš©ν•  일이 μ—†λ‹€λ©΄, μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” κΈ°λŠ₯을 μ‚¬μš©ν•˜λ©΄ λœλ‹€.


5. μ›Ή μŠ€μ½”ν”„

μ›Ή μŠ€μ½”ν”„μ˜ νŠΉμ§•

- μ›Ή μŠ€μ½”ν”„λŠ” μ›Ή ν™˜κ²½μ—μ„œλ§Œ λ™μž‘

- μ›Ή μŠ€μ½”ν”„λŠ” ν”„λ‘œν† νƒ€μž…κ³Ό λ‹€λ₯΄κ²Œ μŠ€ν”„λ¦¬μ΄ ν•΄λ‹Ή μŠ€μ½”ν”„μ˜ μ’…λ£Œμ‹œμ κΉŒμ§€ 관리 (μ’…λ£Œλ©”μ„œλ“œ 호좜)

 

μ›Ή μŠ€μ½”ν”„ μ’…λ₯˜

'request' : HTTPμš”μ²­ ν•˜λ‚˜κ°€ λ“€μ–΄μ˜€κ³  λ‚˜κ°ˆλ•ŒκΉŒμ§€ μœ μ§€λ˜λŠ” μŠ€μ½”ν”„ / 각각의 HTTP μš”μ²­λ§ˆλ‹€ λ³„λ„μ˜ 빈 μΈμŠ€ν„΄μŠ€κ°€ μƒμ„±λ˜κ³  관리됨

'session' : HTTP sessionκ³Ό λ™μΌν•œ 생λͺ…μ£ΌκΈ°λ₯Ό κ°€μ§€λŠ” μŠ€μ½”ν”„

'application' : μ„œλΈ”λ¦Ώ μ»¨ν…μŠ€νŠΈ('ServletContext')와 λ™μΌν•œ 생λͺ…μ£ΌκΈ°λ₯Ό κ°€μ§€λŠ” μŠ€μ½”ν”„

'websocket' : μ›Ή μ†ŒμΌ“κ³Ό λ™μΌν•œ 생면주기λ₯Ό κ°€μ§€λŠ” μŠ€μ½”ν”„

 

ex) request μŠ€μ½”ν”„

HTTP request μš”μ²­ λ‹Ή 각각 ν• λ‹Ήλ˜λŠ” request μŠ€μ½”ν”„


6. request μŠ€μ½”ν”„ 예제 λ§Œλ“€κΈ°

μ›Ή ν™˜κ²½ μΆ”κ°€

- build.gradle

//web 라이브러리 μΆ”κ°€
implementation 'org.springframework.boot:spring-boot-starter-web'

이제 hello.cored.CoreApplication의 mainλ©”μ„œλ“œ μ‹€ν–‰ν•˜λ©΄ μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ 확인

μ°Έκ³ :
spring-boot-starter-web 라이브러리λ₯Ό μΆ”κ°€ν•˜λ©΄ μŠ€ν”„λ§ λΆ€νŠΈλŠ” λ‚΄μž₯ ν†°μΌ“ μ„œλ²„λ₯Ό ν™œμš©ν•΄μ„œ μ›Ή μ„œλ²„μ™€ μŠ€ν”„λ§μ„ ν•¨κ»˜ μ‹€ν–‰μ‹œν‚¨λ‹€.

μ°Έκ³ :
μŠ€ν”„λ§ λΆ€νŠΈλŠ” μ›Ή λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μ—†μœΌλ©΄ μš°λ¦¬κ°€ μ§€κΈˆκΉŒμ§€ ν•™μŠ΅ν•œ AnnotationConfigApplicationContext 을 기반으둜 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ΅¬λ™ν•œλ‹€.
μ›Ή λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μΆ”κ°€λ˜λ©΄ μ›Ήκ³Ό κ΄€λ ¨λœ μΆ”κ°€ μ„€μ •κ³Ό ν™˜κ²½λ“€μ΄ ν•„μš”ν•˜λ―€λ‘œ

> AnnotationConfigServletWebServerApplicationContext λ₯Ό 기반으둜 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ΅¬λ™ν•œλ‹€.

 

request μŠ€μ½”ν”„ 예제 개발

 - λ™μ‹œμ— μ—¬λŸ¬ HTTP μš”μ²­μ΄ 였면 μ •ν™•νžˆ μ–΄λ–€ μš”μ²­μ΄ 남긴 λ‘œκ·ΈμΈμ§€ ꡬ뢄 어렀움

- μ΄λŸ΄λ•Œ μ‚¬μš©ν•˜κΈ° 쒋은 것이 request μŠ€μ½”ν”„

[d06b992f...] request scope bean create
[d06b992f...] [http://localhost:8080/log-demo] controller test
[d06b992f...] [http://localhost:8080/log-demo] service id = testId
[d06b992f...] request scope bean close

- κΈ°λŒ€ν•˜λŠ” 곡톡 포맷 : [UUID][requestURL]{message}

- UUIDλ₯Ό μ‚¬μš©ν•΄μ„œ HTTP μš”μ²­ ꡬ뢄

- requestURL 정보도 μΆ”κ°€λ‘œ λ„£μ–΄μ„œ μ–΄λ–€ URL을 μš”μ²­ν•΄μ„œ 남은 λ‘œκ·ΈμΈμ§€ 확인

 

μ½”λ“œ

MyLogger.class

γ„΄hello.cored.common

@Component
@Scope(value = "request")
public class MyLogger {
  private String uuid;
  private String requestURL;
  
  public void setRequestURL(String requestURL) {
    this.requestURL = requestURL;
  }
  
  public void log(String message) {
    System.out.println("[" + uuid + "]" + "[" + requestURL + "] " + message); 
  }
  
  @PostConstruct
  public void init() {
    uuid = UUID.randomUUID().toString();
    System.out.println("[" + uuid + "] request scope bean create:" + this);
  }
  
  @PreDestroy
  public void close() {
    System.out.println("[" + uuid + "] request scope bean close:" + this);
  }
}
- 둜그λ₯Ό 좜λ ₯ν•˜κΈ° μœ„ν•œ MyLogger ν΄λž˜μŠ€μ΄λ‹€.
- @Scope(value = "request") λ₯Ό μ‚¬μš©ν•΄μ„œ request μŠ€μ½”ν”„λ‘œ μ§€μ •ν–ˆλ‹€.
- 이제 이 λΉˆμ€ HTTP μš”μ²­ λ‹Ή
ν•˜λ‚˜μ”© μƒμ„±λ˜κ³ , HTTP μš”μ²­μ΄ λλ‚˜λŠ” μ‹œμ μ— μ†Œλ©Έλœλ‹€.
- 이 빈이 μƒμ„±λ˜λŠ” μ‹œμ μ— μžλ™μœΌλ‘œ @PostConstruct μ΄ˆκΈ°ν™” λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄μ„œ uuidλ₯Ό μƒμ„±ν•΄μ„œ μ €μž₯ν•΄λ‘”λ‹€.
- 이 λΉˆμ€ HTTP μš”μ²­ λ‹Ή ν•˜λ‚˜μ”© μƒμ„±λ˜λ―€λ‘œ, uuidλ₯Ό μ €μž₯해두면 λ‹€λ₯Έ HTTP μš”μ²­κ³Ό ꡬ뢄할 수 μžˆλ‹€.
- 이 빈이 μ†Œλ©Έλ˜λŠ” μ‹œμ μ— @PreDestroy λ₯Ό μ‚¬μš©ν•΄μ„œ μ’…λ£Œ λ©”μ‹œμ§€λ₯Ό 남긴닀.

- requestURL은 이 빈이 μƒμ„±λ˜λŠ” μ‹œμ μ—λŠ” μ•Œ 수 μ—†μœΌλ―€λ‘œ, μ™ΈλΆ€μ—μ„œ setter둜 μž…λ ₯ λ°›λŠ”λ‹€.

LogDemoController  

γ„΄hello.cored.web

@Controller
@RequiredArgsConstructor
public class LogDemoController {
  private final LogDemoService logDemoService;
  private final MyLogger myLogger;
  
  @RequestMapping("log-demo")
  @ResponseBody
  public String logDemo(HttpServletRequest request) {
    String requestURL = request.getRequestURL().toString();
    myLogger.setRequestURL(requestURL);
    myLogger.log("controller test");
    logDemoService.logic("testId");
    return "OK";
  } 
}
- λ‘œκ±°κ°€ 잘 μž‘λ™ν•˜λŠ”μ§€ ν™•μΈν•˜λŠ” ν…ŒμŠ€νŠΈμš© μ»¨νŠΈλ‘€λŸ¬λ‹€.
- μ—¬κΈ°μ„œ HttpServletRequestλ₯Ό ν†΅ν•΄μ„œ μš”μ²­ URL을 λ°›μ•˜λ‹€.

- requestURL κ°’ http://localhost:8080/log-demo
   μ΄λ ‡κ²Œ 받은 requestURL 값을 myLogger에 μ €μž₯ν•΄λ‘”λ‹€.
- myLogger
λŠ” HTTP μš”μ²­ λ‹Ή 각각 κ΅¬λΆ„λ˜λ―€λ‘œ λ‹€λ₯Έ HTTP μš”μ²­ λ•Œλ¬Έμ— 값이 μ„žμ΄λŠ” 걱정은 ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€.
- μ»¨νŠΈλ‘€λŸ¬μ—μ„œ controller testλΌλŠ” 둜그λ₯Ό 남긴닀.

LogDemoService

γ„΄hello.cored.logdemo

@Service
@RequiredArgsConstructor
public class LogDemoService {
  private final MyLogger myLogger;
  public void logic(String id) {
  myLogger.log("service id = " + id);
  } 
}
- λΉ„μ¦ˆλ‹ˆμŠ€ 둜직이 μžˆλŠ” μ„œλΉ„μŠ€ κ³„μΈ΅μ—μ„œλ„ 둜그λ₯Ό 좜λ ₯
- μ€‘μš”) request scopeλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  νŒŒλΌλ―Έν„°λ‘œ 이 λͺ¨λ“  정보λ₯Ό μ„œλΉ„μŠ€ 계측에 λ„˜κΈ΄λ‹€λ©΄, νŒŒλΌλ―Έν„°κ°€ λ§Žμ•„μ„œ 지저뢄해진닀.
           λ” λ¬Έμ œλŠ” requestURL 같은 μ›Ήκ³Ό κ΄€λ ¨λœ 정보가 μ›Ήκ³Ό κ΄€λ ¨μ—†λŠ” μ„œλΉ„μŠ€ κ³„μΈ΅κΉŒμ§€ λ„˜μ–΄κ°€κ²Œ λœλ‹€.
           μ›Ήκ³Ό κ΄€λ ¨λœ 뢀뢄은 μ»¨νŠΈλ‘€λŸ¬κΉŒμ§€λ§Œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.
           μ„œλΉ„μŠ€ 계측은 μ›Ή κΈ°μˆ μ— μ’…μ†λ˜μ§€ μ•Šκ³ , 가급적 μˆœμˆ˜ν•˜κ²Œ μœ μ§€ν•˜λŠ” 것이 μœ μ§€λ³΄μˆ˜ κ΄€μ μ—μ„œ μ’‹λ‹€.
- request scope
의 MyLogger 덕뢄에 이런 뢀뢄을 νŒŒλΌλ―Έν„°λ‘œ λ„˜κΈ°μ§€ μ•Šκ³ , MyLogger의 λ©€λ²„λ³€μˆ˜μ— μ €μž₯ν•΄μ„œ μ½”λ“œμ™€ 계측을 κΉ”λ”ν•˜κ²Œ μœ μ§€ν•  수 μžˆλ‹€.

* request μŠ€μ½”ν”„ λΉˆμ€ 아직 μƒμ„±λ˜μ§€ μ•ŠλŠ”λ‹€. 이 λΉˆμ€ μ‹€μ œ 고객의 μš”μ²­μ΄ 와야 생성할 수 μžˆλ‹€!


7. μŠ€μ½”ν”„μ™€ Provider

μœ„ 문제 ν•΄κ²°)

1) ObjdectProvider μ‚¬μš©

@Controller
@RequiredArgsConstructor
public class LogDemoController {
  private final LogDemoService logDemoService;
  private final ObjectProvider<MyLogger> myLoggerProvider;
  
  @RequestMapping("log-demo")
  @ResponseBody
  public String logDemo(HttpServletRequest request) {
    String requestURL = request.getRequestURL().toString();
    MyLogger myLogger = myLoggerProvider.getObject();
    myLogger.setRequestURL(requestURL);
    myLogger.log("controller test");
    logDemoService.logic("testId");
    return "OK";
  } 
}
@Service
@RequiredArgsConstructor
public class LogDemoService {
  private final ObjectProvider<MyLogger> myLoggerProvider;
  
  public void logic(String id) {
    MyLogger myLogger = myLoggerProvider.getObject();
    myLogger.log("service id = " + id);
  } 
}
- ObjectProvider 덕뢄에 ObjectProvider.getObject() λ₯Ό ν˜ΈμΆœν•˜λŠ” μ‹œμ κΉŒμ§€ request scope 빈의 생성을 지연할 수 μžˆλ‹€.
- ObjectProvider.getObject() λ₯Ό ν˜ΈμΆœν•˜μ‹œλŠ” μ‹œμ μ—λŠ” HTTP μš”μ²­μ΄ μ§„ν–‰μ€‘μ΄λ―€λ‘œ request scope 빈의 생성이 정상 μ²˜λ¦¬λœλ‹€.
- ObjectProvider.getObject() λ₯Ό LogDemoController , LogDemoService μ—μ„œ 각각 ν•œλ²ˆμ”© λ”°λ‘œ ν˜ΈμΆœν•΄λ„ 같은 HTTP μš”μ²­μ΄λ©΄ 같은 μŠ€ν”„λ§ 빈이 λ°˜ν™˜λœλ‹€!

8. μŠ€μ½”ν”„μ™€ Proxy

μœ„ 문제 ν•΄κ²°)

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
  public class MyLogger {
}
핡심)
- proxyMode = ScopedProxyMode.TARGET_CLASS
λ₯Ό μΆ”κ°€.
  적용 λŒ€μƒμ΄ μΈν„°νŽ˜μ΄μŠ€κ°€ μ•„λ‹Œ 클래슀면 TARGET_CLASS λ₯Ό 선택
  적용 λŒ€μƒμ΄ μΈν„°νŽ˜μ΄μŠ€λ©΄
INTERFACES λ₯Ό 선택

- μ΄λ ‡κ²Œ ν•˜λ©΄ MyLogger의 κ°€μ§œ ν”„λ‘μ‹œ 클래슀λ₯Ό λ§Œλ“€μ–΄λ‘κ³  HTTP request와 상관 없이 κ°€μ§œ ν”„λ‘μ‹œ 클래슀λ₯Ό λ‹€λ₯Έ λΉˆμ— 미리 μ£Όμž…ν•΄ λ‘˜ 수 μžˆλ‹€.
@Controller
@RequiredArgsConstructor
public class LogDemoController {
  private final LogDemoService logDemoService;
  private final MyLogger myLogger;
  
  @RequestMapping("log-demo")
  @ResponseBody
  public String logDemo(HttpServletRequest request) {
    String requestURL = request.getRequestURL().toString();
    myLogger.setRequestURL(requestURL);
    myLogger.log("controller test");
    logDemoService.logic("testId");
    return "OK";
  } 
}
@Service
@RequiredArgsConstructor
public class LogDemoService {
  private final MyLogger myLogger;
  public void logic(String id) {
   myLogger.log("service id = " + id);
  }
}

μ›Ή μŠ€μ½”ν”„μ™€ ν”„λ‘μ‹œ λ™μž‘ 원리

CGLIBλΌλŠ” 라이브러리둜 λ‚΄ 클래슀λ₯Ό 상속 받은 κ°€μ§œ ν”„λ‘μ‹œ 객체λ₯Ό λ§Œλ“€μ–΄μ„œ μ£Όμž…ν•œλ‹€.

- @Scope 의 proxyMode = ScopedProxyMode.TARGET_CLASS) λ₯Ό μ„€μ •ν•˜λ©΄ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” CGLIBλΌλŠ” λ°”μ΄νŠΈμ½”λ“œλ₯Ό μ‘°μž‘ν•˜λŠ” 라이브러리λ₯Ό μ‚¬μš©ν•΄μ„œ, MyLoggerλ₯Ό 상속받은 κ°€μ§œ ν”„λ‘μ‹œ 객체λ₯Ό μƒμ„±ν•œλ‹€.
- κ²°κ³Όλ₯Ό 확인해보면 μš°λ¦¬κ°€ λ“±λ‘ν•œ μˆœμˆ˜ν•œ MyLogger ν΄λž˜μŠ€κ°€ μ•„λ‹ˆλΌ MyLogger$ $EnhancerBySpringCGLIB μ΄λΌλŠ” 클래슀둜 λ§Œλ“€μ–΄μ§„ 객체가 λŒ€μ‹  λ“±λ‘λœ 것을 확인할 수 μžˆλ‹€.
- 그리고 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— "myLogger"λΌλŠ” μ΄λ¦„μœΌλ‘œ μ§„μ§œ λŒ€μ‹ μ— 이 κ°€μ§œ ν”„λ‘μ‹œ 객체λ₯Ό λ“±λ‘ν•œλ‹€.

- ac.getBean("myLogger", MyLogger.class) 둜 μ‘°νšŒν•΄λ„ ν”„λ‘μ‹œ 객체가 μ‘°νšŒλ˜λŠ” 것을 확인할 수 μžˆλ‹€.
- κ·Έλž˜μ„œ μ˜μ‘΄κ΄€κ³„ μ£Όμž…λ„ 이 κ°€μ§œ ν”„λ‘μ‹œ 객체가 μ£Όμž…λœλ‹€.

κ°€μ§œ ν”„λ‘μ‹œ κ°μ²΄λŠ” μš”μ²­μ΄ 였면 κ·Έλ•Œ λ‚΄λΆ€μ—μ„œ μ§„μ§œ λΉˆμ„ μš”μ²­ν•˜λŠ” μœ„μž„ 둜직이 λ“€μ–΄μžˆλ‹€.

- κ°€μ§œ ν”„λ‘μ‹œ κ°μ²΄λŠ” 내뢀에 μ§„μ§œ myLoggerλ₯Ό μ°ΎλŠ” 방법을 μ•Œκ³  μžˆλ‹€.
- ν΄λΌμ΄μ–ΈνŠΈκ°€ myLogger.logic() 을 ν˜ΈμΆœν•˜λ©΄ 사싀은 κ°€μ§œ ν”„λ‘μ‹œ 객체의 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œ 것이닀.
- κ°€μ§œ ν”„λ‘μ‹œ κ°μ²΄λŠ” request μŠ€μ½”ν”„μ˜ μ§„μ§œ myLogger.logic() λ₯Ό ν˜ΈμΆœν•œλ‹€.

- κ°€μ§œ ν”„λ‘μ‹œ κ°μ²΄λŠ” 원본 클래슀λ₯Ό 상속 λ°›μ•„μ„œ λ§Œλ“€μ–΄μ‘ŒκΈ° λ•Œλ¬Έμ— 이 객체λ₯Ό μ‚¬μš©ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈ μž…μž₯μ—μ„œλŠ” 사싀 원본인지 μ•„λ‹Œμ§€λ„ λͺ¨λ₯΄κ²Œ, λ™μΌν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλ‹€(λ‹€ν˜•μ„±)

λ™μž‘ 정리

- CGLIBλΌλŠ” 라이브러리둜 λ‚΄ 클래슀λ₯Ό 상속 받은 κ°€μ§œ ν”„λ‘μ‹œ 객체λ₯Ό λ§Œλ“€μ–΄μ„œ μ£Όμž…ν•œλ‹€.
- 이 κ°€μ§œ ν”„λ‘μ‹œ κ°μ²΄λŠ” μ‹€μ œ μš”μ²­μ΄ 였면 κ·Έλ•Œ λ‚΄λΆ€μ—μ„œ μ‹€μ œ λΉˆμ„ μš”μ²­ν•˜λŠ” μœ„μž„ 둜직이 λ“€μ–΄μžˆλ‹€.
- κ°€μ§œ ν”„λ‘μ‹œ κ°μ²΄λŠ” μ‹€μ œ request scopeμ™€λŠ” 관계가 μ—†λ‹€. κ·Έλƒ₯ κ°€μ§œμ΄κ³ , 내뢀에 λ‹¨μˆœν•œ μœ„μž„ 둜직만 있고, 싱글톀 처럼 λ™μž‘ν•œλ‹€.

νŠΉμ§• 정리

- ν”„λ‘μ‹œ 객체 덕뢄에 ν΄λΌμ΄μ–ΈνŠΈλŠ” 마치 싱글톀 λΉˆμ„ μ‚¬μš©ν•˜λ“―μ΄ νŽΈλ¦¬ν•˜κ²Œ request scopeλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.
- 사싀 Providerλ₯Ό μ‚¬μš©ν•˜λ“ , ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•˜λ“  핡심 μ•„μ΄λ””μ–΄λŠ” μ§„μ§œ 객체 쑰회λ₯Ό κΌ­ ν•„μš”ν•œ μ‹œμ κΉŒμ§€ μ§€μ—°μ²˜λ¦¬ ν•œλ‹€λŠ” 점이닀.
- 단지 μ• λ…Έν…Œμ΄μ…˜ μ„€μ • λ³€κ²½λ§ŒμœΌλ‘œ 원본 객체λ₯Ό ν”„λ‘μ‹œ 객체둜 λŒ€μ²΄ν•  수 μžˆλ‹€. 이것이 λ°”λ‘œ λ‹€ν˜•μ„±κ³Ό DI μ»¨ν…Œμ΄λ„ˆκ°€ 가진 큰 강점이닀.
- κΌ­ μ›Ή μŠ€μ½”ν”„κ°€ μ•„λ‹ˆμ–΄λ„ ν”„λ‘μ‹œλŠ” μ‚¬μš©ν•  수 μžˆλ‹€.

주의점

- 마치 싱글톀을 μ‚¬μš©ν•˜λŠ” 것 κ°™μ§€λ§Œ λ‹€λ₯΄κ²Œ λ™μž‘ν•˜κΈ° λ•Œλ¬Έμ— κ²°κ΅­ μ£Όμ˜ν•΄μ„œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.
- 이런 νŠΉλ³„ν•œ scopeλŠ” κΌ­ ν•„μš”ν•œ κ³³μ—λ§Œ μ΅œμ†Œν™”ν•΄μ„œ μ‚¬μš©ν•˜μž, λ¬΄λΆ„λ³„ν•˜κ²Œ μ‚¬μš©ν•˜λ©΄ μœ μ§€λ³΄μˆ˜ν•˜κΈ° μ–΄λ €μ›Œμ§„λ‹€.

 

  • 넀이버 λΈ”λŸ¬κ·Έ κ³΅μœ ν•˜κΈ°
  • 넀이버 λ°΄λ“œμ— κ³΅μœ ν•˜κΈ°
  • 페이슀뢁 κ³΅μœ ν•˜κΈ°
  • μΉ΄μΉ΄μ˜€μŠ€ν† λ¦¬ κ³΅μœ ν•˜κΈ°