hades

[Spring] ์Šคํ”„๋ง ๊ธฐ์ดˆ (2) ๋ณธ๋ฌธ

๐Ÿƒ๐Ÿป‍โ™‚๏ธ ๊ธฐ๋ณธํ›ˆ๋ จ/Spring

[Spring] ์Šคํ”„๋ง ๊ธฐ์ดˆ (2)

hades1 2024. 7. 24. 10:15

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์™€ ์Šคํ”„๋ง ๋นˆ

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ

ApplicationContext๋Š” ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์ด์ž ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค. ๋”ฐ๋ผ์„œ ๋‹คํ˜•์„ฑ์ด ์ ์šฉ๋œ๋‹ค. AnnotationConfigApplicationContext๋„ ์žˆ๊ณ , ๋‹ค๋ฅธ ApplicationContext๋“ค๋„ ์žˆ๋‹ค.

์„ค์ •์ž„์„ ๋‚˜ํƒ€๋‚ด๋Š” @Configuration์ด ๋ถ™์€ AppConfig.class๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํ•˜๋Š” AnnotationConfigApplicationContext์„ ์ƒ์„ฑํ•˜๋ฉด, @Bean์ด ๋ถ™์€ ๊ฒƒ๋“ค์ด ์Šคํ”„๋ง ์ €์žฅ์†Œ์— ์ €์žฅ๋œ๋‹ค. ๋นˆ ์ด๋ฆ„์€ ๋ฉ”์†Œ๋“œ ์ด๋ฆ„, ๋นˆ ๊ฐ์ฒด๋Š” return ๊ฐ’์ด๋‹ค.

 

๋นˆ์˜ ์ด๋ฆ„์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์ด๋ฉฐ, ํ•ญ์ƒ ๋‹ค๋ฅธ ์ด๋ฆ„์„ ๋ถ€์—ฌํ•ด์•ผ ํ•œ๋‹ค.

 

3. ์Šคํ”„๋ง ๋นˆ ์˜์กด ๊ด€๊ณ„ ์„ค์ •

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ์„ค์ • ์ •๋ณด๋ฅผ ์ฐธ๊ณ ํ•ด์„œ ์˜์กด๊ด€๊ณ„๋ฅผ ์ฃผ์ž…ํ•œ๋‹ค. (์„œ๋กœ ๋‹ค๋ฅธ memberRepository๋ฅผ ๊ฐ€๋ฅดํ‚ค๋Š” ๊ฑฐ ์•„๋‹Œ๊ฐ€..?)->@Configuration๊ณผ ์‹ฑ๊ธ€ํ†ค ์ฐธ์กฐ

 

์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก๋œ ๋ชจ๋“  ๋นˆ ์กฐํšŒ

package hello.core.beanfind;

import hello.core.AppConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ApplicationContextInfoTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("๋ชจ๋“  ๋นˆ ์ถœ๋ ฅ")
    void findAllBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) { // iter ์น˜๊ณ , Tab
            Object bean = ac.getBean(beanDefinitionName);
            System.out.println("name = " + beanDefinitionName + " object = " + bean);
        }
    }

    @Test
    @DisplayName("์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ ์ถœ๋ ฅ")
    void findApplicationAllBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames(); // Bean์˜ ์ด๋ฆ„๋“ค์„ ๋‹ค ๊ฐ€์ ธ์˜ด
        for (String beanDefinitionName : beanDefinitionNames) { // iter ์น˜๊ณ , Tab
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);   // Bean์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ด

            if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){ // <-> ROLE_INFRASTRUCTURE ์Šคํ”„๋ง ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋นˆ
                Object bean = ac.getBean(beanDefinitionName);
                System.out.println("name = " + beanDefinitionName + " object = " + bean);
            }

        }
    }
}

BeanDefinition.ROLE_APPLICATION์€ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๋“ฑ๋กํ•œ ๋นˆ์ด๊ณ , ROLE_INFRASTRUCTURE ์Šคํ”„๋ง ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋นˆ์ด๋‹ค.

 

์Šคํ”„๋ง ๋นˆ ์กฐํšŒ - ๊ธฐ๋ณธ

ac.getBean(์ด๋ฆ„, ํƒ€์ž…)
ac.getBean(ํƒ€์ž…)
@Test
@DisplayName("๋นˆ ์ด๋ฆ„์œผ๋กœ ์ฐพ๊ธฐ")
void findBeanByName(){
    MemberService memberService = ac.getBean("memberService", MemberService.class);
//        System.out.println("memberService = " + memberService);
//        System.out.println("memberService.getClass() = " + memberService.getClass());
    Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

memberService๋Š” AppConfig์—์„œ memberService ๋ฉ”์†Œ๋“œ์˜ return ๊ฒฐ๊ณผ์ธ memberServiceImpl์ด๋‹ค.

 

@Test
@DisplayName("ํƒ€์ž…์œผ๋กœ ์ฐพ๊ธฐ")
void findBeanByType(){
    MemberService memberService = ac.getBean(MemberService.class);
    Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

ํƒ€์ž…๋งŒ์œผ๋กœ๋„ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

 

@Test
@DisplayName("๊ตฌ์ฒด ํƒ€์ž…์œผ๋กœ ์ฐพ๊ธฐ")
void findBeanBySpecificType(){
    MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
    Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

๊ตฌ์ฒด ํƒ€์ž…์œผ๋กœ ์ฐพ์„ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ์—ญํ• ๊ณผ ๊ตฌํ˜„์„ ๋ถ„๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ์›์น™์— ๋”ฐ๋ผ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ์•„๋‹ˆ๋‹ค. 

 

@Test
@DisplayName("๋นˆ ์ด๋ฆ„์œผ๋กœ ์ฐพ๊ธฐ ์‹คํŒจ")
void findBeanByNameX(){
    MemberService memberService = ac.getBean("fail", MemberService.class);
}

์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋ก๋˜์ง€ ์•Š์€ ์ด๋ฆ„์œผ๋กœ getBean์„ ํ•  ๊ฒฝ์šฐ, ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

์Šคํ”„๋ง ๋นˆ ์กฐํšŒ - ๋™์ผํ•œ ํƒ€์ž…์ด ๋‘˜ ์ด์ƒ

@Test
@DisplayName("๋™์ผ ํƒ€์ž… ์กด์žฌ ์‹œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ")
void findBeanByTypeDuplicate(){
    MemberRepository memberRepository = ac.getBean(MemberRepository.class);
}

@Configuration
static class TestConfig {
    @Bean
    public MemberRepository memberRepository1(){
        return new MemoryMemberRepository();
    }

    @Bean
    public MemberRepository memberRepository2(){
        return new MemoryMemberRepository();
    }
}

๋™์ผํ•œ ํƒ€์ž…์„ ๊ฐ–๋Š” ๋นˆ์ด ์—ฌ๋Ÿฌ ๊ฐœ์ผ ๋•Œ, getBean์„ ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

@Test
@DisplayName("๋™์ผ ํƒ€์ž… ์กด์žฌ ์‹œ ์ด๋ฆ„์„ ์ง€์ •ํ•˜๋ฉด ์˜ค๋ฅ˜ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ")
void findBeanByTypeDuplicate2(){
    MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
}

์ด๋ฆ„์„ ์ง€์ •ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋กœ์จ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ํด๋ž˜์Šค๊ฐ€ ๊ฐ™์•„๋„ ์ด๋ฆ„์ด ๋‹ค๋ฅด๋ฉด, ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค.

 

@Test
@DisplayName("ํŠน์ • ํƒ€์ž…์„ ๊ฐ€์ง€๋Š” ๋นˆ์„ ๋ชจ๋‘ ์กฐํšŒํ•˜๊ธฐ")
void findAllBeanByType(){
    Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
    for (String key : beansOfType.keySet()) {
        System.out.println("key = " + key + " value = " + beansOfType.get(key));;
    }
    System.out.println("beansOfType = " + beansOfType);
    Assertions.assertEquals(beansOfType.size(), 2);
}

beansOfType return ๊ฐ’์˜ key๋Š” bean์˜ ์ด๋ฆ„, value๋Š” bean์ด๋‹ค.

 

์Šคํ”„๋ง ๋นˆ ์กฐํšŒ - ์ƒ์† ๊ด€๊ณ„๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ

๋ถ€๋ชจ ํƒ€์ž…์„ ์กฐํšŒํ•˜๋ฉด, ์ž์‹ ํƒ€์ž…๋„ ์กฐํšŒ๋œ๋‹ค.

๋ชจ๋“  ์ž๋ฐ” ๊ฐ์ฒด์˜ ์ตœ๊ณ  ๋ถ€๋ชจ๋Š” Object์ธ๋ฐ, Object๋ฅผ ์กฐํšŒํ•˜๋ฉด ๋ชจ๋“  ์Šคํ”„๋ง ๋นˆ์ด ์กฐํšŒ๋œ๋‹ค.

 

@Test
@DisplayName("๋ถ€๋ชจ ํƒ€์ž…์œผ๋กœ getBeanํ•  ์‹œ ์ž์‹์ด ๋‘˜ ์ด์ƒ ์žˆ์œผ๋ฉด, ์ค‘๋ณต ์˜ค๋ฅ˜ ๋ฐœ์ƒ")
void findBeanByParentTypeDuplicate() {
	//DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
    assertThrows(NoUniqueBeanDefinitionException.class, () ->
	ac.getBean(DiscountPolicy.class));
}

์ธํ„ฐํŽ˜์ด์Šค์ธ DiscountPolicy์˜ ์ž์‹์ด ๋‘˜ ์ด์ƒ์ผ ๋•Œ, getBean์„ ํ•˜๊ฒŒ ๋˜๋ฉด, ์–ด๋Š ์ž์‹์„ ์„ ํƒํ•ด์•ผ ํ• ์ง€ ๋ชจ๋ฅด๋ฏ€๋กœ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

@Test
@DisplayName("๋ถ€๋ชจ ํƒ€์ž…์œผ๋กœ ์กฐํšŒ์‹œ, ์ž์‹์ด ๋‘˜ ์ด์ƒ ์žˆ์œผ๋ฉด, ๋นˆ ์ด๋ฆ„์„ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค")
void findBeanByParentTypeBeanName() {
	DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy",
	DiscountPolicy.class);
	assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}

์ž์‹์˜ ๋นˆ ์ด๋ฆ„์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ํ•ด๊ฒฐ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

@Test
@DisplayName("ํŠน์ • ํ•˜์œ„ ํƒ€์ž…์œผ๋กœ ์กฐํšŒ")
void findBeanBySubType() {
	RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
	assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
}

์ž์‹๋“ค์˜ ํƒ€์ž…์ด ๋ชจ๋‘ ๋‹ค๋ฅด๋‹ค๋ฉด, ํŠน์ • ํ•˜์œ„ ํƒ€์ž…์œผ๋กœ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

 

@Test
@DisplayName("๋ถ€๋ชจ ํƒ€์ž…์œผ๋กœ ๋ชจ๋‘ ์กฐํšŒํ•˜๊ธฐ")
void findAllBeanByParentType() {
	Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
	assertThat(beansOfType.size()).isEqualTo(2);
	for (String key : beansOfType.keySet()) {
		System.out.println("key = " + key + " value =" +
		beansOfType.get(key));
	}
}

getBean์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ , ๋ชจ๋“  ๋นˆ์ด ๋‚˜์˜จ๋‹ค.

 

BeanFactory์™€ ApplicationContext

 

BeanFactory

  • ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์˜ ์ตœ์ƒ์œ„ ์ธํ„ฐํŽ˜์ด์Šค๋‹ค.
  • ์Šคํ”„๋ง ๋นˆ์„ ๊ด€๋ฆฌํ•˜๊ณ  ์กฐํšŒํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•œ๋‹ค.
  • getBean() ์„ ์ œ๊ณตํ•œ๋‹ค.
  • ์ง€๊ธˆ๊นŒ์ง€ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ–ˆ๋˜ ๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ๋Šฅ์€ BeanFactory๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

 

ApplicationContext

BeanFactory ๊ธฐ๋Šฅ์„ ๋ชจ๋‘ ์ƒ์†๋ฐ›์•„์„œ ์ œ๊ณตํ•œ๋‹ค.
๋นˆ์„ ๊ด€๋ฆฌํ•˜๊ณ  ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ธฐ๋Šฅ์„ BeanFactory๊ฐ€ ์ œ๊ณตํ•ด์ฃผ๋Š”๋ฐ, ์™œ ํ•„์š”ํ•˜์ง€?
์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ๋นˆ์„ ๊ด€๋ฆฌํ•˜๊ณ  ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ์€ ๋ฌผ๋ก ์ด๊ณ , ์ˆ˜ ๋งŽ์€ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ํ•„์š”ํ•˜๋‹ค.

 

๋ฉ”์‹œ์ง€์†Œ์Šค๋ฅผ ํ™œ์šฉํ•œ ๊ตญ์ œํ™” ๊ธฐ๋Šฅ
์˜ˆ๋ฅผ ๋“ค์–ด์„œ ํ•œ๊ตญ์—์„œ ๋“ค์–ด์˜ค๋ฉด ํ•œ๊ตญ์–ด๋กœ, ์˜์–ด๊ถŒ์—์„œ ๋“ค์–ด์˜ค๋ฉด ์˜์–ด๋กœ ์ถœ๋ ฅ

 

ํ™˜๊ฒฝ๋ณ€์ˆ˜
๋กœ์ปฌ, ๊ฐœ๋ฐœ, ์šด์˜๋“ฑ์„ ๊ตฌ๋ถ„ํ•ด์„œ ์ฒ˜๋ฆฌ

 

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฒคํŠธ
์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๊ณ  ๊ตฌ๋…ํ•˜๋Š” ๋ชจ๋ธ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ง€์›

 

ํŽธ๋ฆฌํ•œ ๋ฆฌ์†Œ์Šค ์กฐํšŒ
ํŒŒ์ผ, ํด๋ž˜์ŠคํŒจ์Šค, ์™ธ๋ถ€ ๋“ฑ์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์กฐํšŒ

 

๊ฒฐ๋ก ์ ์œผ๋กœ, BeanFactory์™€ ๋‹ค๋ฅธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›๋Š” ApplicationContext๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

๋‹ค์–‘ํ•œ ์„ค์ • ํ˜•์‹ ์ง€์› - ์ž๋ฐ” ์ฝ”๋“œ, XML

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ๋‹ค์–‘ํ•œ ํ˜•์‹์˜ ์„ค์ • ์ •๋ณด๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

์š”์ฆ˜์€ AnnotationConfigApplicationContext๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.

 

xml๋กœ ์„ค์ •ํ•˜๊ธฐ

package hello.core.xml;

import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class XmlAppContext {
    @Test
    void xmlAppContext(){
        ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }
}

์„ค์ •์ •๋ณด๋ฅผ Xml๋กœ๋ถ€ํ„ฐ ์–ป๋Š” ApplicationContext

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="memberService" class="hello.core.member.MemberServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository" />
    </bean>
    <bean id="memberRepository"
          class="hello.core.member.MemoryMemberRepository" />
    <bean id="orderService" class="hello.core.order.OrderServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository" />
        <constructor-arg name="discountPolicy" ref="discountPolicy" />
    </bean>
    <bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy" />
</beans>

orderService๋ผ๋Š” ๋นˆ์€ OrderServiceImpl ์ƒ์„ฑ์ž๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , memberRepository์™€ discountPolicy๋ฅผ ์ „๋‹ฌ๋ฐ›๋Š”๋‹ค.

  • id : ์Šคํ”„๋ง ๋นˆ ์ด๋ฆ„
  • class : ์Šคํ”„๋ง ๋นˆ value
  • contructor-arg : name์€ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„, ref๋Š” ์ฐธ์กฐํ•  id

 

์Šคํ”„๋ง ๋นˆ ์„ค์ • ๋ฉ”ํƒ€ ์ •๋ณด - BeanDefinition

์Šคํ”„๋ง์ด ๋‹ค์–‘ํ•œ ํ˜•์‹์œผ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์ด์œ ๋Š” BeanDefinition์ด๋ผ๋Š” ์ถ”์ƒํ™”๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ๋นˆ ์„ค์ • ๋ฉ”ํƒ€์ •๋ณด์ธ BeanDefinition๋งŒ ์•Œ๋ฉด ๋œ๋‹ค. 

 

AnnotationConfigApplicationContext ๋Š” AnnotatedBeanDefinitionReader ๋ฅผ ์ด์šฉํ•˜์—ฌ AppConfig.class ๋ฅผ ์ฝ๊ณ  BeanDefinition ์„ ์ƒ์„ฑํ•œ๋‹ค.

GenericXmlApplicationContext ๋Š” XmlBeanDefinitionReader ๋ฅผ ์‚ฌ์šฉํ•ด์„œ appConfig.xml ์„ค์ • ์ •๋ณด๋ฅผ ์ฝ๊ณ  BeanDefinition ์„ ์ƒ์„ฑํ•œ๋‹ค.

 

์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์‹ฑ๊ธ€ํ†ค

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋ณดํ†ต ์—ฌ๋Ÿฌ ๊ณ ๊ฐ์ด ๋™์‹œ์— ์š”์ฒญ์„ ํ•œ๋‹ค. 

 

package hello.core.singleton;

import hello.core.AppConfig;
import hello.core.member.MemberService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class SingletonTest {

    @Test
    @DisplayName("์Šคํ”„๋ง์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ DI ์ปจํ…Œ์ด๋„ˆ")
    void pureContainer(){
        AppConfig appConfig = new AppConfig();
        MemberService memberService1 = appConfig.memberService();
        MemberService memberService2 = appConfig.memberService();
        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        Assertions.assertThat(memberService1).isNotSameAs(memberService2);
    }
}

 

์Šคํ”„๋ง์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด(= @Bean์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ , getBean์œผ๋กœ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์œผ๋ฉด), memberService๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. 

ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํ•˜๋‚˜๋งŒ ์ƒ์„ฑํ•˜๊ณ , ๊ทธ๊ฒƒ์„ ๊ณต์œ ํ•˜๋„๋ก ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๊ฒƒ์ด ์‹ฑ๊ธ€ํ†ค์ด๋‹ค.

 

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด

ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋”ฑ 1๊ฐœ๋งŒ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค.

private ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์™ธ๋ถ€์—์„œ ์ž„์˜๋กœ new ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ•˜๋„๋ก ๋ง‰์•„์•ผ ํ•œ๋‹ค.

package hello.core.singleton;

public class SingletonService {
    private static final SingletonService instance = new SingletonService();

    public static SingletonService getInstance(){
        return instance;
    }

    private SingletonService(){
        
    }
    
    public void logic(){
        System.out.println("์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด ๋กœ์ง ํ˜ธ์ถœ");
    }
}

๊ฐ์ฒด๋ฅผ static ์˜์—ญ์— ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•ด๋†“๋Š”๋‹ค.

 

๊ฐ์ฒด๋ฅผ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ getInstance()๋ฅผ ํ†ตํ•ด์„œ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

ํ•จ๋ถ€๋กœ ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•˜๋„๋ก private๋กœ ๋ง‰์•„๋‘์—ˆ๋‹ค.

 

์œ„ ๋ฐฉ๋ฒ•์€ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ค‘ ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๊ณ  ์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.

 

@Test
@DisplayName("์‹ฑ๊ธ€ํ†ค ํŒจํ†ค์„ ์ ์šฉํ•œ ๊ฐ์ฒด ์‚ฌ์šฉ")
void singletonServiceTest(){
    SingletonService singletonService1 = SingletonService.getInstance();
    SingletonService singletonService2 = SingletonService.getInstance();

    System.out.println("singletonService1 = " + singletonService1);
    System.out.println("singletonService2 = " + singletonService2);

    Assertions.assertThat(singletonService1).isSameAs(singletonService2);
}

ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋งŒ์„ ๊ฐ€๋ฅดํ‚ค๋ฏ€๋กœ, singletonService1๊ณผ singletonService2๋Š” ๊ฐ™๋‹ค.

equalTo๋Š” ๊ฐ’์ด ๊ฐ™์€์ง€, sameAs๋Š” ์ฐธ์กฐ๊ฐ€ ๊ฐ™์€์ง€ ๋น„๊ตํ•œ๋‹ค.

 

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์˜ ๋ฌธ์ œ์ 

  • ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋งŽ์ด ๋“ค์–ด๊ฐ„๋‹ค.
  • ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์‚ฌ์šฉํ•  ๋•Œ ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กดํ•ด์„œ DIP(์˜์กด ์—ญ์ „ ์›์น™)์„ ์œ„๋ฐ˜ํ•œ๋‹ค. (AppConfig์— ๊ตฌ์ฒดํ™”๊ฐ€ ๋“ค์–ด๊ฐ)
  • OCP๋ฅผ ์œ„๋ฐ˜ํ•  ๊ฐ€๋Šฅ์„ฑ๋„ ๋†’์•„์ง€๊ฒŒ ๋œ๋‹ค.
  • ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
  • private ์ƒ์„ฑ์ž๋กœ ์ž์‹ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ธฐ ์–ด๋ ต๋‹ค.
  • ์œ ์—ฐ์„ฑ์ด ๋–จ์–ด์ง„๋‹ค.
  • ์•ˆํ‹ฐํŒจํ„ด์œผ๋กœ ๋ถˆ๋ฆฌ๊ธฐ๋„ ํ•œ๋‹ค.

์ฐธ๊ณ ) DIP : ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ์ง€, ๊ตฌ์ฒดํ™”์— ์˜์กดํ•˜๋ฉด ์•ˆ ๋œ๋‹ค

 

์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ฉด์„œ, ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค. ์Šคํ”„๋ง ๋นˆ์€ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌ๋˜๋Š” ๋นˆ์ด๋‹ค.

@Test
@DisplayName("์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์™€ ์‹ฑ๊ธ€ํ†ค")
void springContainer(){
    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);

    Assertions.assertThat(memberService1).isSameAs(memberService2);
}

์Šคํ”„๋ง ๋นˆ์€ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์— singletonService1๊ณผ singletonService2๋Š” ๊ฐ™๋‹ค.

 

์‹ฑ๊ธ€ํ†ค ๋ฐฉ์‹์˜ ์ฃผ์˜์ 

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์ด๋“ , ์Šคํ”„๋ง์˜ ์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ๋“  ์—ฌ๋Ÿฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•˜๋‚˜์˜ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ฒŒ ์„ค๊ณ„ํ•˜๋ฉด ์•ˆ๋œ๋‹ค. ์ฆ‰, ๊ฐ’์„ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๊ฒŒ ํ•ด์•ผ ํ•œ๋‹ค. ํ•„๋“œ ๋Œ€์‹ ์— ์ž๋ฐ”์—์„œ ๊ณต์œ ๋˜์ง€ ์•Š๋Š” ์ง€์—ญ๋ณ€์ˆ˜, ํŒŒ๋ผ๋ฏธํ„ฐ, ThreadLocal ๋“ฑ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

package hello.core.singleton;

public class StatefulService {
	private int price; // ์ƒํƒœ ์ €์žฅ
    
    public void order(String name, int price) {
		System.out.println("name = " + name + " price = " + price);
		this.price = price; // ์ƒํƒœ ๋ณ€๊ฒฝ
	}

	public int getPrice() {
    	return price;
	}
}
package hello.core.singleton;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

import static org.junit.jupiter.api.Assertions.*;

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("userA", 10000);
        statefulService2.order("userB", 20000);

        int price = statefulService1.getPrice();

        Assertions.assertThat(statefulService1.getPrice()).isEqualTo(20000);
    }

    static class TestConfig {
        @Bean
        public StatefulService statefulService(){
            return new StatefulService();
        }
    }
}

์Šคํ”„๋ง์— ์˜ํ•ด statefulService๋ผ๋Š” ์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. A๊ฐ€ 10000์› ์งœ๋ฆฌ ์ฃผ๋ฌธ์„ ํ•˜๊ณ , B๊ฐ€ 20000์› ์งœ๋ฆฌ ์ฃผ๋ฌธ์„ ํ•œ ํ›„์— A๊ฐ€ ์ฃผ๋ฌธํ•œ ๊ฐ€๊ฒฉ์„ ์กฐํšŒํ•˜๋ฉด, ์˜๋„์™€ ๋‹ค๋ฅด๊ฒŒ 20000์›์ด ๋„์ถœ๋œ๋‹ค. ์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์ค‘๊ฐ„์— ๊ฐ’์„ ์ˆ˜์ •ํ•˜๋Š” ๊ณผ์ •์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒ๋˜๋Š” ๋ฌธ์ œ์ด๋‹ค.

 

โ˜… โ˜… โ˜… ์Šคํ”„๋ง์€ ๋นˆ์€ ๋ฌด์ƒํƒœ๋กœ ์„ค๊ณ„ํ•˜์ž! โ˜… โ˜… โ˜…

package hello.core.singleton;

public class StatelessService {
    public void order(String name, int price){
        System.out.println("name = " + name + " price " + price);
        return price;
    }
}
@Test
void statefulServiceSingleton(){
    ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
    StatefulService statefulService1 = ac.getBean(StatefulService.class);
    StatefulService statefulService2 = ac.getBean(StatefulService.class);

    int userAPrice = statefulService1.order("userA", 10000);
    int userBPrice = statefulService2.order("userB", 20000);

    Assertions.assertThat(userAPrice).isEqualTo(10000);
}

๊ฐ’์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๊ฒŒ ํ•˜๊ณ , ํ•„๋“œ ๋Œ€์‹ ์— ์ž๋ฐ”์—์„œ ๊ณต์œ ๋˜์ง€ ์•Š๋Š” ์ง€์—ญ๋ณ€์ˆ˜๋ฅผ ์ด์šฉํ•œ ๊ฒฐ๊ณผ์ด๋‹ค.

 

@Configuration๊ณผ ์‹ฑ๊ธ€ํ†ค

AppConfig์˜ memberService์™€ orderService๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, ์„œ๋กœ ๋‹ค๋ฅธ MemoryMemberRepository๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

public class ConfigurationSingletonTest {
    @Test
    @DisplayName("๋‘ ๊ฐœ๊ฐ€ ๊ฐ™์•„์•ผ ํ•จ")
    void configurationTest(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
        OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
        MemberRepository memberRepository1 = memberService.getMemberRepository();
        MemberRepository memberRepository2 = orderService.getMemberRepository();

        System.out.println("memberRepository1 = " + memberRepository1);
        System.out.println("memberRepository2 = " + memberRepository2);

        Assertions.assertThat(memberRepository1).isSameAs(memberRepository2);
    }
}

ํ™•์ธ ๊ฒฐ๊ณผ ๋‘˜์€ ๊ฐ™๋‹ค. 

 

โ˜…โ˜… getMemberRepository๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค์ธ MemberService์—๋Š” ์„ ์–ธํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ, ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•œ๋‹ค.

 

@Configuration๊ณผ ๋ฐ”์ดํŠธ์ฝ”๋“œ ์กฐ์ž‘์˜ ๋งˆ๋ฒ•

@Test
void configurationDeep(){
    ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    AppConfig bean = ac.getBean(AppConfig.class);
    System.out.println("bean.getClass() = " + bean.getClass());
}

AppConfig์—์„œ ๋๋‚˜์ง€ ์•Š๊ณ , $$SpringCGLIB~ ๋ผ๋Š” ๊ฒƒ์ด ๋ถ™๋Š”๋‹ค.

 appConfig ๋นˆ์—๋Š” AppConfig๋ฅผ ์ƒ์†๋ฐ›์•„ ์กฐ์ž‘ํ•œ AppConfig@CGLIB๊ฐ€ ๋“ฑ๋ก๋˜์–ด ์žˆ๋‹ค.

 

@Configuration์ด ์—†์–ด๋„ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก์€ ๋˜์ง€๋งŒ, ์‹ฑ๊ธ€ํ†ค์ด ๊นจ์ง„๋‹ค. ๊ทธ๋ž˜์„œ ์“ฐ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

 

์ปดํฌ๋„ŒํŠธ ์Šค์บ”

์ปดํฌ๋„ŒํŠธ ์Šค์บ”๊ณผ ์˜์กด๊ด€๊ณ„ ์ž๋™ ์ฃผ์ž…

์Šคํ”„๋ง ๋นˆ์„ ๋“ฑ๋กํ•  ๋•Œ๋Š” @Bean์ด๋‚˜ XML์˜ <bean>์„ ํ†ปํ•ด์„œ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ, ๋“ฑ๋กํ•  ์Šคํ”„๋ง ๋นˆ์ด ๋งŽ์•„์ง€๋ฉด, ์ผ์ผ์ด ๋“ฑ๋กํ•˜๊ธฐ๋„ ๊ท€์ฐฎ๊ณ , ์„ค์ • ์ •๋ณด๋„ ์ปค์ง€๊ณ , ๋ˆ„๋ฝํ•˜๋Š” ๋ฌธ์ œ๋„ ๋ฐœ์ƒํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ์Šคํ”„๋ง์€ ์„ค์ • ์ •๋ณด๊ฐ€ ์—†์–ด๋„ ์ž๋™์œผ๋กœ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์ด๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. 

 

package hello.core;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(
        excludeFilters = @ComponentScan.Filter(type= FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {

}

@Configuration -> ์„ค์ • ๊ด€๋ จ ํด๋ž˜์Šค๋กœ ๋งŒ๋“ ๋‹ค. @Configuration ๋‚ด๋ถ€์— @Component๊ฐ€ ํฌํ•จ๋˜๋ฏ€๋กœ, ์ด๊ฒƒ์ด ๋ถ™์€ ํด๋ž˜์Šค๋„ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋œ๋‹ค.

@ComponentScan -> ํด๋ž˜์Šค๊ฐ€ ComponentScan์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š”๋ฐ, Filter๋ฅผ ์ด์šฉํ•˜๋ฉด ๋‹ค๋ฅธ Configuration์ด ํฌํ•จ๋˜์ง€ ์•Š๋„๋ก ์ œ์™ธ์‹œํ‚จ๋‹ค.

 

์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•  ํด๋ž˜์Šค ์œ„์— @Component๋ฅผ ๋ถ™์ด๊ณ , ์˜์กด์„ฑ ์ฃผ์ž…์ด ํ•„์š”ํ•œ ๊ณณ์— @Autowired๋ฅผ ๋ถ™์ธ๋‹ค.

package hello.core.scan;

import hello.core.AutoAppConfig;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AutoAppConfigTest {
    @Test
    void basicScan(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);

        MemberService memberService = ac.getBean(MemberService.class);
        Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }
}

 

@ComponentScan ์€ @Component ๊ฐ€ ๋ถ™์€ ๋ชจ๋“  ํด๋ž˜์Šค๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.


์ด๋•Œ ์Šคํ”„๋ง ๋นˆ์˜ ๊ธฐ๋ณธ ์ด๋ฆ„์€ ํด๋ž˜์Šค๋ช…์„ ์‚ฌ์šฉํ•˜๋˜ ๋งจ ์•ž๊ธ€์ž๋งŒ ์†Œ๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, ๊ธฐ๋ณธ์ ์œผ๋กœ MemberServiceImpl ํด๋ž˜์Šค๋ผ๋ฉด memberServiceImpl๋กœ ์ €์žฅ๋œ๋‹ค.

๋งŒ์•ฝ ์Šคํ”„๋ง ๋นˆ์˜ ์ด๋ฆ„์„ ์ง์ ‘ ์ง€์ •ํ•˜๊ณ  ์‹ถ์œผ๋ฉด, @Component("memberService2")์ฒ˜๋Ÿผ ์ด๋ฆ„์„ ๋ถ€์—ฌํ•˜๋ฉด ๋œ๋‹ค.

 

์ƒ์„ฑ์ž์— @Autowired๋ฅผ ์ง€์ •ํ•˜๋ฉด, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ํƒ€์ž…์ด ๊ฐ™์€(์ž์‹ ํฌํ•จ) ์Šคํ”„๋ง ๋นˆ์„ ์ฐพ์•„์„œ ์ฃผ์ž…ํ•œ๋‹ค.

 

 

ํƒ์ƒ‰ ์œ„์น˜์™€ ๊ธฐ๋ณธ ์Šค์บ” ๋Œ€์ƒ

package hello.core;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(
        basePackages = "hello.core.member",
        excludeFilters = @ComponentScan.Filter(type= FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {

}

basePackages๋Š” ํƒ์ƒ‰ํ•  ํŒจํ‚ค์ง€์˜ ์‹œ์ž‘ ์œ„์น˜๋ฅผ ์ง€์ •ํ•˜๋Š”๋ฐ, ์ด ์œ„์น˜๋ถ€ํ„ฐ ํ•˜์œ„ ํŒจํ‚ค์ง€๋ฅผ ๋ชจ๋‘ ํƒ์ƒ‰ํ•œ๋‹ค. ์œ„์™€ ๊ฐ™์ด ์ง€์ •ํ•ด์ค€๋‹ค๋ฉด, order์™€ discount ๊ด€๋ จ ์Šคํ”„๋ง ๋นˆ์€ ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š”๋‹ค.

 

backPackages๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด, @ComponentScan์ด ๋ถ™์€ ์„ค์ • ์ •๋ณด ํด๋ž˜์Šค๊ฐ€ ์žˆ๋Š” ํŒจํ‚ค์ง€๊ฐ€ ์‹œ์ž‘ ์œ„์น˜๊ฐ€ ๋œ๋‹ค.

 

โ˜…๊ด€๋ก€์ ์œผ๋กœ, ์„ค์ • ์ •๋ณด ํด๋ž˜์Šค๋ฅผ ํ”„๋กœ์ ํŠธ ์ตœ์ƒ๋‹จ์— ๋‘๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. @SpringBootApplication์€ @ComponentScan์„ ํฌํ•จํ•˜๋ฏ€๋กœ, ์ด๋ฅผ ํฌํ•จํ•˜๋Š” ํŒŒ์ผ์€ ํ”„๋กœ์ ํŠธ ์ตœ์ƒ๋‹จ์— ๋‘”๋‹ค.

 

์ปดํฌ๋„ŒํŠธ ์Šค์บ” ๊ธฐ๋ณธ ๋Œ€์ƒ

@Component : ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์˜ ๋Œ€์ƒ์ด๊ณ , ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋จ.

@Controller : ์Šคํ”„๋ง MVC ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‚ฌ์šฉ

@Service : ์Šคํ”„๋ง ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์‚ฌ์šฉ๋ฐ์ดํ„ฐ ๊ณ„์ธต์˜ ์˜ˆ์™ธ๋ฅผ ์Šคํ”„๋ง ์˜ˆ์™ธ๋กœ ๋ณ€ํ™˜ํ•ด์ค€๋‹ค.(์•„์ง์€ ๋ชจ๋ฅด๊ฒ ๋‹ค)

@Repository : ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์—์„œ ์‚ฌ์šฉ

@Configuration : ์Šคํ”„๋ง ์„ค์ • ์ •๋ณด์—์„œ ์‚ฌ์šฉ, @Bean์ด ๋ถ™์–ด์žˆ์–ด ๋“ฑ๋ก๋˜๋Š” ์Šคํ”„๋ง ๋นˆ์ด ์‹ฑ๊ธ€ํ†ค์„ ์œ ์ง€ํ•˜๋„๋ก ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ

 

์ฐธ๊ณ ) ์‚ฌ์‹ค ์• ๋…ธํ…Œ์ด์…˜์—๋Š” ์ƒ์†๊ด€๊ณ„๋ผ๋Š” ๊ฒƒ์ด ์—†๋‹ค. ์• ๋…ธํ…Œ์ด์…˜์ด ํŠน์ • ์• ๋…ธํ…Œ์ด์…˜์„ ํฌํ•จํ•˜๋Š” ๊ฒƒ์„ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ์ž๋ฐ” ์–ธ์–ด๊ฐ€ ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ์€ ์•„๋‹ˆ๊ณ , ์Šคํ”„๋ง์ด ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

 

ํ•„ํ„ฐ (์ค‘์š”๋„ ๋‚ฎ์Œ)

includeFilters : ์ปดํฌ๋„ŒํŠธ ์Šค์บ” ๋Œ€์ƒ์„ ์ถ”๊ฐ€๋กœ ์ง€์ •, @Component๋ผ๊ณ  ๋ถ™์€ ๊ฒƒ ์ด์™ธ์— ์ถ”๊ฐ€๋กœ ์Šค์บ”ํ•  ๊ฒƒ

excludeFilters : ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์—์„œ ์ œ์™ธํ•  ๋Œ€์ƒ์„ ์ง€์ •

 

package hello.core.scan.filter;

import java.lang.annotation.*;

@Target({ElementType.TYPE}) // ํด๋ž˜์Šค ๋ ˆ๋ฒจ์— ๋ถ™๋Š”๋‹ค๋Š” ๋œป
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {

}
package hello.core.scan.filter;

import java.lang.annotation.*;

@Target({ElementType.TYPE}) // ํด๋ž˜์Šค ๋ ˆ๋ฒจ์— ๋ถ™๋Š”๋‹ค๋Š” ๋œป
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {

}

ํฌํ•จํ•  ์• ๋…ธํ…Œ์ด์…˜๊ณผ ํฌํ•จํ•˜์ง€ ์•Š์„ ์• ๋…ธํ…Œ์ด์…˜์„ ๊ฐ๊ฐ ํ•˜๋‚˜์”ฉ ๋งŒ๋“ ๋‹ค.

 

package hello.core.scan.filter;

@MyIncludeComponent
public class beanA {
}
package hello.core.scan.filter;

@MyExcludeComponent
public class beanB {
}

 

package hello.core.scan.filter;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

public class ComponentFilterAppConfigTest {
    @Test
    void filterScan(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
        BeanA beanA = ac.getBean(BeanA.class);
        Assertions.assertThat(beanA).isNotNull();

        org.junit.jupiter.api.Assertions.assertThrows(NoSuchBeanDefinitionException.class, ()->ac.getBean(BeanB.class));
    }


    @Configuration
    @ComponentScan(
            includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
            excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
    )
    static class ComponentFilterAppConfig{

    }
}

beanA๋Š” ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋˜์–ด getBeanํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, beanB๋Š” ๋“ฑ๋ก๋  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ getBeanํ• ์‹œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

FilterType Option

ANNOTATION : ๊ธฐ๋ณธ๊ฐ’, ์• ๋…ธํ…Œ์ด์…˜์„ ์ธ์‹ํ•ด์„œ ๋™์ž‘ํ•œ๋‹ค.
ex) org.example.SomeAnnotation
ASSIGNABLE_TYPE: ์ง€์ •ํ•œ ํƒ€์ž…๊ณผ ์ž์‹ ํƒ€์ž…์„ ์ธ์‹ํ•ด์„œ ๋™์ž‘ํ•œ๋‹ค.
ex) org.example.SomeClass
ASPECTJ: AspectJ ํŒจํ„ด ์‚ฌ์šฉ
ex) org.example..*Service+
REGEX: ์ •๊ทœ ํ‘œํ˜„์‹
ex) org\.example\.Default.*
CUSTOM: `TypeFilter` ์ด๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์„œ ์ฒ˜๋ฆฌ
ex) org.example.MyTypeFilter

 

์ฐธ๊ณ ) @Component ๋ฉด ์ถฉ๋ถ„ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, includeFilters ๋ฅผ ์‚ฌ์šฉํ•  ์ผ์€ ๊ฑฐ์˜ ์—†๋‹ค๊ณ  ํ•œ๋‹ค. excludeFilters๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ด์œ ๋กœ ๊ฐ„ํ˜น ์‚ฌ์šฉํ•  ๋•Œ๊ฐ€ ์žˆ์ง€๋งŒ ๋งŽ์ง€๋Š” ์•Š๋‹ค.


ํŠนํžˆ, ์ตœ๊ทผ ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์„ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š”๋ฐ, ๊ฐœ์ธ์ ์œผ๋กœ๋Š” ์˜ต์…˜์„ ๋ณ€๊ฒฝํ•˜๋ฉด์„œ ์‚ฌ์šฉํ•˜๊ธฐ ๋ณด๋‹ค๋Š” ์Šคํ”„๋ง์˜ ๊ธฐ๋ณธ ์„ค์ •์— ์ตœ๋Œ€ํ•œ ๋งž์ถ”์–ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•˜์‹ ๋‹ค๊ณ  ํ•œ๋‹ค.

 

์ค‘๋ณต ๋“ฑ๋ก๊ณผ ์ถฉ๋Œ

1. ์ž๋™ ๋นˆ ๋“ฑ๋ก vs ์ž๋™ ๋นˆ ๋“ฑ๋ก -> ์ถฉ๋Œ ์˜ค๋ฅ˜

2. ์ˆ˜๋™ ๋นˆ ๋“ฑ๋ก vs ์ž๋™ ๋นˆ ๋“ฑ๋ก -> ์ˆ˜๋™ ๋นˆ ์šฐ์„ , ํ•˜์ง€๋งŒ  ์ตœ๊ทผ ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ์ˆ˜๋™ ๋นˆ ๋“ฑ๋ก๊ณผ ์ž๋™ ๋นˆ ๋“ฑ๋ก์ด ์ถฉ๋Œ๋‚˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋„๋ก ๊ธฐ๋ณธ ๊ฐ’์„ ๋ฐ”๊พธ์—ˆ๋‹ค. (@SpringBootApplication์œผ๋กœ ์‹คํ–‰ํ•  ๋•Œ)

 

์˜์กด ๊ด€๊ณ„ ์ž๋™ ์ฃผ์ž…

์˜์กด๊ด€๊ณ„ ์ฃผ์ž… ๋ฐฉ๋ฒ•

์ƒ์„ฑ์ž ์ฃผ์ž…

์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด์„œ ์˜์กด ๊ด€๊ณ„๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

 

โ˜…๋ถˆ๋ณ€โ˜… ์ด ์ค‘์š”ํ•œ๋ฐ, ์ƒ์„ฑ์ž ํ˜ธ์ถœ์‹œ์ ์— ๋งค๊ฐœ๋ณ€์ˆ˜๋“ค์ด ๋”ฑ 1๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ์ด ๋ณด์žฅ๋œ๋‹ค. ์ฆ‰, ์ฃผ์ž…๋˜๋Š” ์‹œ์ ์„ ์ œ์™ธํ•˜๊ณ ๋Š” ๋‹ค๋ฅธ ๊ฒƒ์œผ๋กœ ์ฃผ์ž…ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

 

final์ด ๋ถ™์œผ๋ฉด ์ƒ์„ฑ์ž ์‹คํ–‰์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋ฌด์กฐ๊ฑด ์ง€์ •๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๊ณ , ์ƒ์„ฑ์ž ์‹คํ–‰์ด ์™„๋ฃŒ๋˜๋ฉด ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค. ์ƒ์ƒ์ž ์ฃผ์ž…์„ ํ†ตํ•ด์„œ ์ƒ์„ฑ์ž ์‹คํ–‰ ๊ณผ์ •์—์„œ ํŒŒ๋ผ๋ฏธํ„ฐ ์ƒ์„ฑ์ž๊ฐ€ ์ฃผ์ž…๋˜์–ด ์ง€์ •๋œ๋‹ค. ๋‹ค๋ฅธ ์ฃผ์ž… ๋ฐฉ์‹๋“ค์€ ์ƒ์„ฑ์ž ์‹คํ–‰ ์ดํ›„์— ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ, final์„ ์ด์šฉํ•  ์ˆ˜ ์—†๋‹ค. final๊ณผ ์ƒ์„ฑ์ž ์ฃผ์ž… ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ˆ„๋ฝ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

 

์ƒ์„ฑ์ž๊ฐ€ ํ•˜๋‚˜์ด๋ฉด, @Autowired๊ฐ€ ์•ˆ๋ถ™์–ด๋„ ์˜์กด ๊ด€๊ณ„ ์ฃผ์ž…์ด ์ผ์–ด๋‚œ๋‹ค.

 

์˜ต์…˜ ์ฒ˜๋ฆฌ

package hello.core.autowired;

import hello.core.member.Member;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;

import java.util.Optional;

public class AutowiredTest {

    @Test
    void AutowiredOption(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(AutowiredConfig.class);
    }

    @Configuration
    static class AutowiredConfig {
        @Autowired(required = false)
        public void setNoBean1(Member member) {
            System.out.println("setNoBean1 = " + member);
        }

        @Autowired
        public void setNoBean2(@Nullable Member member){
            System.out.println("setNoBean2 = " + member);
        }

        @Autowired
        public void setNoBean3(Optional<Member> member){
            System.out.println("setNoBean3 = " + member);
        }
    }
}

@Autowired์—์„œ required ์˜ต์…˜์˜ ๊ธฐ๋ณธ๊ฐ’์ด true๋กœ ๋˜์–ด ์žˆ์–ด์„œ ์ฃผ์ž…ํ•  ๊ฒƒ์„ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†์œผ๋ฉด, ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. required ์˜ต์…˜์ด false์ด๊ณ , ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ์—†์œผ๋ฉด ์ˆ˜์ •์ž ๋ฉ”์„œ๋“œ ์ž์ฒด๊ฐ€ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.

@Nullable : ์ž๋™ ์ฃผ์ž…ํ•  ๋Œ€์ƒ์ด ์—†์œผ๋ฉด, null์ด ์ž…๋ ฅ๋œ๋‹ค.

Optional<> : ์ž๋™ ์ฃผ์ž…ํ•  ๋Œ€์ƒ์ด ์—†์œผ๋ฉด Optional.empty ๊ฐ€ ์ž…๋ ฅ๋œ๋‹ค.

 

๋กฌ๋ณต๊ณผ ์ตœ์‹  ํŠธ๋ Œ๋“œ

๋กฌ๋ณต์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ•„์š”ํ•œ ์„ค์ •

//lombok ์„ค์ • ์ถ”๊ฐ€ ์‹œ์ž‘
configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}
//lombok ์„ค์ • ์ถ”๊ฐ€ ๋
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	//lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€ ์‹œ์ž‘
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testCompileOnly 'org.projectlombok:lombok'
	testAnnotationProcessor 'org.projectlombok:lombok'
	//lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€ ๋
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

build.gradle์— ์œ„ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , plugin์—์„œ lombok ์„ค์น˜ํ•˜๊ณ , Annotation Processors์—์„œ Enable annotation processing ์ฒดํฌํ•˜๋ฉด ๋กฌ๋ณต์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋กฌ๋ณต์˜ ๊ฐ„๋‹จํ•œ ์‚ฌ์šฉ

package hello.core;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class HelloLombok {
    private int age;
    private String name;

    public static void main(String[] args) {
        HelloLombok helloLombok = new HelloLombok();
        helloLombok.setName("์‹ ์งฑ๊ตฌ");

        System.out.println("name = " + helloLombok.getName());
        System.out.println(helloLombok);
    }
}

@Getter, @Setter๋ฅผ ๋ถ™์ด๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ Getter, Setter๊ฐ€ ๊ตฌํ˜„๋œ๋‹ค.

 

@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
}

@RequiredArgsConstructor๋ฅผ ํด๋ž˜์Šค ์œ„์— ๋ถ™์—ฌ์ฃผ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ์œ„ ์ƒ์„ฑ์ž ์ฝ”๋“œ๋ฅผ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค. Ctrl + F12๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ƒ์„ฑ์ž๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

@Autowired ํ•„๋“œ ๋ช…, @Qualifier, @Primary

์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ๋Š” ๋นˆ์ด 2๊ฐœ ์ด์ƒ -> ๋ฌธ์ œ ๋ฐœ์ƒ!

FixedDiscountPolicy์™€ RateDiscountPolicy ๋ชจ๋‘ @Component๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

@Autowired ๋งค์นญ

@Autowired๋Š” ํƒ€์ž…์œผ๋กœ ๋งค์นญ์„ ์‹œ๋„ํ•˜๊ณ , ์ด๋•Œ ์—ฌ๋Ÿฌ ๋นˆ์ด ์žˆ์œผ๋ฉด, ํ•„๋“œ ์ด๋ฆ„, ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„์œผ๋กœ ๋นˆ ์ด๋ฆ„์„ ์ถ”๊ฐ€ ๋งค์นญ ์‹œ๋„ํ•œ๋‹ค.

@Autowired
private DiscountPolicy rateDiscountPolicy;

@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy rateDiscountPolicy) {
	this.memberRepository = memberRepository;
	this.discountPolicy = rateDiscountPolicy;
}

์œ„๋Š” ํ•„๋“œ(ํด๋ž˜์Šค ๋‚ด๋ถ€ ๋ฉค๋ฒ„) ์ด๋ฆ„ ๋งค์นญ, ์•„๋ž˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„ ๋งค์นญ์œผ๋กœ ๋ชจ๋‘ Test๋ฅผ ํ†ต๊ณผํ•œ๋‹ค. -> ์ด๋ฆ„์„ ๋ณด๊ณ  ๋งค์นญํ•œ๋‹ค.

 

@Qualifier

์‹ค์ œ ๋นˆ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๋ณ„๋ช…์„ ์ถ”๊ฐ€ํ•œ๋‹ค. @Qualifier ๋ผ๋ฆฌ ๋งค์นญํ•˜๊ณ , ์—†์œผ๋ฉด ๋นˆ์—์„œ ์ฐพ๊ณ , ๊ทธ๊ฒƒ๋„ ์•ˆ๋˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

๊ฐ™์€ ๋ถ€๋ชจ๋ฅผ ๊ฐ€์ง€๋Š” ํด๋ž˜์Šค ์ค‘ ํ•˜๋‚˜์—  @Qualifier("mainDiscountPolicy")๋ฅผ ๋ถ™์ด๋ฉด,  @Qualifier("mainDiscountPolicy")๊ฐ€ ๋ถ™์€ ์ž์‹ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

package hello.core.discount;

import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy{
    double discountRate = 0.1;

    @Override
    public int discount(Member member, int price) {
        if (member.getGrade() == Grade.VIP){
            return (int)(price * discountRate);
        }
        else{
            return 0;
        }
    }
}
@Component
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
	
    ...
}

@Qualifier๋Š” @Qualifier๋ฅผ ์ฐพ๋Š” ์šฉ๋„๋กœ๋งŒ ์‚ฌ์šฉํ•˜์ž!!

 

@Primary

์šฐ์„ ์ˆœ์œ„๋ฅผ ์ง€์ •ํ•œ๋‹ค. @Component๋ฅผ ๋‹ค ๋ถ™์—ฌ๋†“๊ณ , @Primary๋ฅผ ๋ถ™์ด๋ฉด ๋งค์šฐ ํŽธํ•  ๋“ฏํ•˜๋‹ค.

 

์šฐ์„  ์ˆœ์œ„

@Primary๋Š” FixedDiscountPolicy์—, @Qualifier๋Š” RateDiscountPolicy์— ๋ถ™์–ด์žˆ๊ณ , @Qualifier๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์ž์„ธํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” @Qualifier๊ฐ€ ์šฐ์„ ๋œ๋‹ค.

 

์• ๋…ธํ…Œ์ด์…˜ ์ง์ ‘ ๋งŒ๋“ค๊ธฐ

@Qualifier("mainDiscountPolicy") ๋‚ด๋ถ€์˜ ๋ฌธ์ž์—ด์€ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ๊ฒ€์ฆ๋˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ, ๋ฌธ์ž์—ด์„ mainnDiscountPolicy๋ผ๊ณ  ์ž˜๋ชป ์จ๋„ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ์•Œ ์ˆ˜ ์—†๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์• ๋…ธํ…Œ์ด์…˜์„ ์ง์ ‘ ๋งŒ๋“ค์–ด์„œ ๊ทธ ์•ˆ์— @Qualifier๋ฅผ ๋„ฃ์–ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

package hello.core.annotation;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.*;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}

์• ๋…ธํ…Œ์ด์…˜์„ ๋งŒ๋“ค๊ณ , 

package hello.core.order;

import hello.core.annotation.MainDiscountPolicy;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    ...
}

๋งŒ๋“  ์• ๋…ธํ…Œ์ด์…˜์„ ๋ถ™์ธ๋‹ค.

 

์กฐํšŒํ•œ ๋นˆ์ด ๋ชจ๋‘ ํ•„์š”ํ•  ๋•Œ, List, Map

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ• ์ธ์˜ ์ข…๋ฅ˜๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ์— DiscountService์—์„œ๋Š” Policy๋“ค์„ ๋ชจ๋‘ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค.

package hello.core.autowired;

import hello.core.AutoAppConfig;
import hello.core.discount.DiscountPolicy;
import hello.core.member.Grade;
import hello.core.member.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.List;
import java.util.Map;

public class AllBeanTest {
    @Test
    void findAllBean(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
        
        DiscountService discountService = ac.getBean(DiscountService.class);
        Member member = new Member(1L, "userA", Grade.VIP);
        int fixDiscountPrice = discountService.discount(member, 10000, "fixDiscountPolicy");
        Assertions.assertThat(fixDiscountPrice).isEqualTo(1000);

        int rateDiscountPrice = discountService.discount(member, 20000, "rateDiscountPolicy");
        Assertions.assertThat(rateDiscountPrice).isEqualTo(2000);

    }

    static class DiscountService {
        private final Map<String, DiscountPolicy> policyMap;
        private final List<DiscountPolicy> policies;

        @Autowired
        public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
            this.policyMap = policyMap;
            this.policies = policies;
            System.out.println("policyMap = " + policyMap);
            System.out.println("policies = " + policies);
        }

        public int discount(Member member, int price, String discountCode) {
            DiscountPolicy discountPolicy = policyMap.get(discountCode);
            return discountPolicy.discount(member, price);
        }
    }
}

AutoAppConfig์™€ DiscountService๊ฐ€ ์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋ก๋œ๋‹ค.

 

โ˜…AutoAppConfig์— @ComponentScan์ด ์žˆ์œผ๋ฏ€๋กœ, DiscountPolicy๋“ค์„ ์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋กํ•œ๋‹ค!

 

Map๊ณผ List์— ์ž๋™์œผ๋กœ ์ฃผ์ž…์ด ์ผ์–ด๋‚˜๊ณ , DiscountPolicy๋“ค์ด ๋‹ด๊ฒจ ์žˆ๋‹ค.

 

discount์—์„œ ์ „๋‹ฌ๋ฐ›์€ discountCode๋ฅผ key๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์Šคํ„ด์Šค๋ฅผ ์–ป๋Š”๋‹ค.

 

์ž๋™, ์ˆ˜๋™์˜ ์˜ฌ๋ฐ”๋ฅธ ์‹ค๋ฌด ์šด์˜ ๊ธฐ์ค€

ํŽธ๋ฆฌํ•œ ์ž๋™ ๊ธฐ๋Šฅ(์ปดํฌ๋„ŒํŠธ ์Šค์บ”๊ณผ ์ž๋™ ์ฃผ์ž…)์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜์ž!

 

ํŽธ๋ฆฌํ•œ ์ž๋™ ๊ธฐ๋Šฅ์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜์ž
์ง์ ‘ ๋“ฑ๋กํ•˜๋Š” ๊ธฐ์ˆ  ์ง€์› ๊ฐ์ฒด(AOP, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ..)๋Š” ์ˆ˜๋™ ๋“ฑ๋ก
๋‹คํ˜•์„ฑ์„ ์ ๊ทน ํ™œ์šฉํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ์ˆ˜๋™ ๋“ฑ๋ก์„ ๊ณ ๋ฏผํ•ด๋ณด์ž

 

๋นˆ ์ƒ๋ช…์ฃผ๊ธฐ ์ฝœ๋ฐฑ

๋นˆ ์ƒ๋ช…์ฃผ๊ธฐ ์ฝœ๋ฐฑ ์‹œ์ž‘

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜ ํ’€์ด๋‚˜, ๋„คํŠธ์›Œํฌ ์†Œ์ผ“์ฒ˜๋Ÿผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ์ ์— ํ•„์š”ํ•œ ์—ฐ๊ฒฐ์„ ๋ฏธ๋ฆฌ ํ•ด๋‘๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
์ข…๋ฃŒ ์‹œ์ ์— ์—ฐ๊ฒฐ์„ ๋ชจ๋‘ ์ข…๋ฃŒํ•˜๋Š” ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๋ ค๋ฉด, ๊ฐ์ฒด์˜ ์ดˆ๊ธฐํ™”์™€ ์ข…๋ฃŒ ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค.

package hello.core.lifecycle;

public class NetworkClient {
    private String url;

    public NetworkClient() {
        System.out.println("์ƒ์„ฑ์ž ํ˜ธ์ถœ, url = " + url);
        connect();
        call("์ดˆ๊ธฐํ™” ์—ฐ๊ฒฐ ๋ฉ”์„ธ์ง€");
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void connect(){
        System.out.println("connect: " + url);
    }

    public void call(String message){
        System.out.println("call: " + url + " message = " + message);
    }

    public void disconnect(){
        System.out.println("close: " + url);
    }
}
package hello.core.lifecycle;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

public class BeanLifeCycleTest {

    @Test
    public void lifeCycleTest(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
        NetworkClient client = ac.getBean(NetworkClient.class);
        ac.close();
    }

    @Configuration
    static class LifeCycleConfig {
        @Bean
        public NetworkClient networkClient(){
            NetworkClient networkClient = new NetworkClient();
            networkClient.setUrl("http://naver.com");
            return networkClient;
        }
    }
}

์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๊ณผ์ •์—์„œ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, url์„ ์ „๋‹ฌํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ, url์€ ๋‹น์—ฐํžˆ null์ด ๋œ๋‹ค.

 

์Šคํ”„๋ง ๋นˆ์˜ ์ด๋ฒคํŠธ ๋ผ์ดํ”„์‚ฌ์ดํด

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ -> ์Šคํ”„๋ง ๋นˆ ์ƒ์„ฑ -> ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…(์ƒ์„ฑ์ž ์ฃผ์ž…์€ ์ด์™ธ์˜ ์ฃผ์ž…)-> ์ดˆ๊ธฐํ™” ์ฝœ๋ฐฑ -> ์‚ฌ์šฉ -> ์†Œ๋ฉธ์ „ ์ฝœ๋ฐฑ -> ์Šคํ”„๋ง ์ข…๋ฃŒ


์ดˆ๊ธฐํ™” ์ฝœ๋ฐฑ: ๋นˆ์ด ์ƒ์„ฑ๋˜๊ณ , ๋นˆ์˜ ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…์ด ์™„๋ฃŒ๋œ ํ›„ ํ˜ธ์ถœ
์†Œ๋ฉธ์ „ ์ฝœ๋ฐฑ: ๋นˆ์ด ์†Œ๋ฉธ๋˜๊ธฐ ์ง์ „์— ํ˜ธ์ถœ

 

์ธํ„ฐํŽ˜์ด์Šค InitializingBean, DisposableBean (์ง€๊ธˆ์€ ๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ)

package hello.core.lifecycle;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class NetworkClient implements InitializingBean, DisposableBean {
    private String url;

    public NetworkClient() {
        System.out.println("์ƒ์„ฑ์ž ํ˜ธ์ถœ, url = " + url);
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void connect(){
        System.out.println("connect: " + url);
    }

    public void call(String message){
        System.out.println("call: " + url + " message = " + message);
    }

    public void disconnect(){
        System.out.println("close: " + url);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…์ด ๋๋‚˜๋ฉด
        connect();
        call("์ดˆ๊ธฐํ™” ์—ฐ๊ฒฐ ๋ฉ”์„ธ์ง€");
    }

    @Override
    public void destroy() throws Exception {
        disconnect();
    }
}

afterPropertiesSet์€ setter๊ฐ€ ๋๋‚œ ํ›„์— ์‹คํ–‰๋œ๋‹ค. destroy๋Š” ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์˜ ์ข…๋ฃŒ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ์‹คํ–‰๋œ๋‹ค.

 

์ดˆ๊ธฐํ™”, ์†Œ๋ฉธ ์ธํ„ฐํŽ˜์ด์Šค ๋‹จ์ 

์ด ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์Šคํ”„๋ง ์ „์šฉ ์ธํ„ฐํŽ˜์ด์Šค๋‹ค. ํ•ด๋‹น ์ฝ”๋“œ๊ฐ€ ์Šคํ”„๋ง ์ „์šฉ ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กดํ•œ๋‹ค.
์ดˆ๊ธฐํ™”, ์†Œ๋ฉธ ๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค.
๋‚ด๊ฐ€ ์ฝ”๋“œ๋ฅผ ๊ณ ์น  ์ˆ˜ ์—†๋Š” ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

package hello.core.lifecycle;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class NetworkClient{
    private String url;

    public NetworkClient() {
        System.out.println("์ƒ์„ฑ์ž ํ˜ธ์ถœ, url = " + url);
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void connect(){
        System.out.println("connect: " + url);
    }

    public void call(String message){
        System.out.println("call: " + url + " message = " + message);
    }

    public void disconnect(){
        System.out.println("close: " + url);
    }

    public void init(){
        System.out.println("NetworkClient.init");
        connect();
        call("์ดˆ๊ธฐํ™” ์—ฐ๊ฒฐ ๋ฉ”์„ธ์ง€");
    }

    public void close(){
        System.out.println("NetworkClient.close");
        disconnect();
    }
}
@Configuration
static class LifeCycleConfig {

    @Bean(initMethod = "init", destroyMethod = "close")
    public NetworkClient networkClient(){
        NetworkClient networkClient = new NetworkClient();
        networkClient.setUrl("http://naver.com");
        return networkClient;
    }
}

 

์„ค์ • ์ •๋ณด ์‚ฌ์šฉ ํŠน์ง•

  • ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ ์ž์œ ๋กญ๊ฒŒ ์ค„ ์ˆ˜ ์žˆ๋‹ค.
  • ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋˜๋Š” ํด๋ž˜์Šค๊ฐ€ ์Šคํ”„๋ง ์ฝ”๋“œ์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ์„ค์ • ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๋ฅผ ๊ณ ์น  ์ˆ˜ ์—†๋Š” ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋„ ์ดˆ๊ธฐํ™”, ์ข…๋ฃŒ ๋ฉ”์„œ๋“œ๋ฅผ ์ 
    ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • @Bean์œผ๋กœ ๋“ฑ๋กํ•  ๋•Œ๋งŒ ๋ฐœ์ƒํ•œ๋‹ค.

 

์ข…๋ฃŒ ๋ฉ”์„œ๋“œ ์ถ”๋ก 

@Bean์˜ destroyMethod ์†์„ฑ์—๋Š” ์•„์ฃผ ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค.

  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋Œ€๋ถ€๋ถ„ close , shutdown ์ด๋ผ๋Š” ์ด๋ฆ„์˜ ์ข…๋ฃŒ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • @Bean์˜ destroyMethod ๋Š” ๊ธฐ๋ณธ๊ฐ’์ด (inferred) (์ถ”๋ก )์œผ๋กœ ๋“ฑ๋ก๋˜์–ด ์žˆ๋‹ค.
  • ์ด ์ถ”๋ก  ๊ธฐ๋Šฅ์€ close, shutdown ๋ผ๋Š” ์ด๋ฆ„์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ž๋™์œผ๋กœ ํ˜ธ์ถœํ•ด์ค€๋‹ค. ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ์ข…๋ฃŒ ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๋ก ํ•ด์„œ ํ˜ธ์ถœํ•ด์ค€๋‹ค. ๋”ฐ๋ผ์„œ ์ง์ ‘ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด ์ข…๋ฃŒ ๋ฉ”์„œ๋“œ๋Š” ๋”ฐ๋กœ ์ ์–ด์ฃผ์ง€ ์•Š์•„๋„ ์ž˜ ๋™์ž‘ํ•œ๋‹ค.
  • ์ถ”๋ก  ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์‹ซ์œผ๋ฉด destroyMethod="" ์ฒ˜๋Ÿผ ๋นˆ ๊ณต๋ฐฑ์„ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค.

 

์• ๋…ธํ…Œ์ด์…˜ @PostConstruct, @PreDestroy โ˜…โ˜…โ˜…

@PostConstruct
public void init(){
    System.out.println("NetworkClient.init");
    connect();
    call("์ดˆ๊ธฐํ™” ์—ฐ๊ฒฐ ๋ฉ”์„ธ์ง€");
}

@PreDestroy
public void close(){
    System.out.println("NetworkClient.close");
    disconnect();
}

 

@PostConstruct, @PreDestroy ์• ๋…ธํ…Œ์ด์…˜ ํŠน์ง•

  • ์ตœ์‹  ์Šคํ”„๋ง์—์„œ ๊ฐ€์žฅ ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
  • ์• ๋…ธํ…Œ์ด์…˜ ํ•˜๋‚˜๋งŒ ๋ถ™์ด๋ฉด ๋˜๋ฏ€๋กœ ๋งค์šฐ ํŽธ๋ฆฌํ•˜๋‹ค.
  • ์Šคํ”„๋ง์— ์ข…์†์ ์ธ ๊ธฐ์ˆ ์ด ์•„๋‹ˆ๋ผ ์ž๋ฐ” ํ‘œ์ค€์ด๋‹ค. ๋”ฐ๋ผ์„œ, ์Šคํ”„๋ง์ด ์•„๋‹Œ ๋‹ค๋ฅธ ์ปจํ…Œ์ด๋„ˆ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ ์Šค์บ”๊ณผ ์ž˜ ์–ด์šธ๋ฆฐ๋‹ค.
  • ์œ ์ผํ•œ ๋‹จ์ ์€ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋Š” ์ ์šฉํ•˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ดˆ๊ธฐํ™”, ์ข…๋ฃŒ ํ•ด์•ผ ํ•˜๋ฉด
    @Bean์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ž.

 

๋นˆ ์Šค์ฝ”ํ”„

์Šคํ”„๋ง ๋นˆ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์˜ ์‹œ์ž‘๊ณผ ํ•จ๊ป˜ ์ƒ์„ฑ๋˜์–ด์„œ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์œ ์ง€๋œ๋‹ค. ์ด๋Š” ์Šคํ”„๋ง ๋นˆ์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹ฑ๊ธ€ํ†ค ์Šค์ฝ”ํ”„๋กœ ์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์Šค์ฝ”ํ”„๋Š” ๋นˆ์ด ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ์กด์žฌํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์† ์‹œ๊ฐ„์ด๋‹ค.

 

์Šค์ฝ”ํ”„ ์ข…๋ฅ˜

  • ์‹ฑ๊ธ€ํ†ค: ๊ธฐ๋ณธ ์Šค์ฝ”ํ”„, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์˜ ์‹œ์ž‘๊ณผ ์ข…๋ฃŒ๊นŒ์ง€ ์œ ์ง€๋˜๋Š” ๊ฐ€์žฅ ๋„“์€ ๋ฒ”์œ„์˜ ์Šค์ฝ”ํ”„์ด๋‹ค.
  • ํ”„๋กœํ† ํƒ€์ž…: ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ํ”„๋กœํ† ํƒ€์ž… ๋นˆ์˜ ์ƒ์„ฑ๊ณผ ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…๊นŒ์ง€๋งŒ ๊ด€์—ฌํ•˜๊ณ  ๋”๋Š” ๊ด€๋ฆฌํ•˜์ง€ ์•Š๋Š” ๋งค์šฐ ์งง์€ ๋ฒ”์œ„์˜ ์Šค์ฝ”ํ”„์ด๋‹ค.
  • ์›น ๊ด€๋ จ ์Šค์ฝ”ํ”„
    - request: ์›น ์š”์ฒญ์ด ๋“ค์–ด์˜ค๊ณ  ๋‚˜๊ฐˆ๋•Œ ๊นŒ์ง€ ์œ ์ง€๋˜๋Š” ์Šค์ฝ”ํ”„์ด๋‹ค.
    - session: ์›น ์„ธ์…˜์ด ์ƒ์„ฑ๋˜๊ณ  ์ข…๋ฃŒ๋  ๋•Œ ๊นŒ์ง€ ์œ ์ง€๋˜๋Š” ์Šค์ฝ”ํ”„์ด๋‹ค.
    - application: ์›น์˜ ์„œ๋ธ”๋ฆฟ ์ปจํ…์ŠคํŠธ์™€ ๊ฐ™์€ ๋ฒ”์œ„๋กœ ์œ ์ง€๋˜๋Š” ์Šค์ฝ”ํ”„์ด๋‹ค.

๋Œ€๋ถ€๋ถ„์€ ์‹ฑ๊ธ€ํ†ค ๋นˆ์„ ์‚ฌ์šฉํ•œ๋‹ค.

 

ํ”„๋กœํ† ํƒ€์ž… ์Šค์ฝ”ํ”„

์‹ฑ๊ธ€ํ†ค ์Šค์ฝ”ํ”„์˜ ๋นˆ์„ ์กฐํšŒํ•˜๋ฉด ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ํ•ญ์ƒ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค ์Šคํ”„๋ง ๋นˆ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋ฐ˜๋ฉด์— ํ”„๋กœํ† ํƒ€์ž… ์Šค์ฝ”ํ”„๋ฅผ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ์กฐํšŒํ•˜๋ฉด ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ํ•ญ์ƒ ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

ํด๋ผ์ด์–ธํŠธ A๊ฐ€ ํ”„๋กœํ† ํƒ€์ž… ๋นˆ์„ ์š”์ฒญํ•˜๋ฉด, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ํ”„๋กœํ† ํƒ€์ž… ๋นˆ์„ ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…ํ•˜์—ฌ ์ƒ์„ฑํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ A์—๊ฒŒ ์ œ๊ณตํ•˜๊ณ  ๋ฒ„๋ ค๋ฒ„๋ฆฐ๋‹ค. ์ฆ‰ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋” ์ด์ƒ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์Šคํ”„๋ง ๋นˆ์ด ์‚ฌ๋ผ์งˆ ๋•Œ ํ˜ธ์ถœํ•˜๋Š” @PreDestroy๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.

 

package hello.core.scope;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;

public class PrototypeTest {

    @Test
    void prototypeBeanFind() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(prototypeBean.class);

        prototypeBean prototypeBean1 = ac.getBean(prototypeBean.class);
        prototypeBean prototypeBean2 = ac.getBean(prototypeBean.class);
        System.out.println("prototypeBean1 = " + prototypeBean1);
        System.out.println("prototypeBean2 = " + prototypeBean2);
        Assertions.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");
        }
    }
}

@PreDestroy๋กœ ์ง€์ •ํ•œ detroy๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์€ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

์‹ฑ๊ธ€ํ†ค ๋นˆ๊ณผ ํ”„๋กœํ† ํƒ€์ž… ๋นˆ์„ ํ•จ๊ป˜ ์‚ฌ์šฉ์‹œ ๋ฌธ์ œ์ 

์‹ฑ๊ธ€ํ†ค ๋นˆ์ด ํ”„๋กœํ† ํƒ€์ž… ๋นˆ์„ ์ฃผ์ž…๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ๊ฒฐ๊ณผ์ ์œผ๋กœ ๊ฐ™์€ ํ”„๋กœํ† ํƒ€์ž…์„ ์ด์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์€ ์‹ฑ๊ธ€ํ†ค ๋นˆ ์•ˆ์—์„œ ํ”„๋กœํ† ํƒ€์ž… ๋นˆ์„ ์ƒˆ๋กœ ์š”์ฒญํ•˜๊ณ  ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

package hello.core.scope;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;

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

    static class ClientBean {
        private ApplicationContext ac;  // ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ

        public ClientBean(ApplicationContext ac) {	// ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ์ฃผ์ž…
            this.ac = 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");
        }
    }
}

ClientBean์— PrototypeBean์„ ์˜์กด์„ฑ ์ฃผ์ž…(DI)ํ•˜์ง€ ์•Š๊ณ , ClientBean ๋‚ด๋ถ€์—์„œ PrototypeBean์„ ์ฐพ์•„์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์˜์กด์„ฑ ํƒ์ƒ‰, DL(Dependency Lookup)์ด๋ผ๊ณ  ํ•œ๋‹ค. ํ•˜์ง€๋งŒ, ๋ณต์žกํ•ด์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

ObjectProvider, ObjectFactory

static class ClientBean {
    private ObjectProvider<PrototypeBean> prototypeBeanProvider;

    public ClientBean(ObjectProvider<PrototypeBean> prototypeBeanProvider) {
        this.prototypeBeanProvider = prototypeBeanProvider;
    }

    public int logic() {
        PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
        prototypeBean.addCount();
        int count = prototypeBean.getCount();
        return count;
    }
}

ObjectProvider๋Š” ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์—์„œ ์Šคํ”„๋ง ๋นˆ์„ ์š”์ฒญํ•ด์„œ ๋ฐ›์•„์ฃผ๋Š” ๋Œ€๋ฆฌ์ž ์—ญํ• ์„ ํ•œ๋‹ค. ํ”„๋กœํ† ํƒ€์ž…์„ ์œ„ํ•ด์„œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

 

ObjectProvider<PrototypeBean>์ด๋ฏ€๋กœ, getObject()๋ฅผ ํ•˜๊ฒŒ ๋˜๋ฉด ํ”„๋กœํ† ํƒ€์ž…์„ ์ฐพ๋Š”๋ฐ, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ํ”„๋กœํ† ํƒ€์ž…์— ๋Œ€ํ•œ ์š”์ฒญ์ด ๋“ค์–ด์™”์„ ๋•Œ, ํ”„๋กœํ† ํƒ€์ž…์„ ์ƒˆ๋กœ ์ƒ์„ฑํ•ด์„œ ์ œ๊ณตํ•œ๋‹ค.

 

ObjectProvider๋Š” ์Šคํ”„๋ง์ด ๋งŒ๋“ค์–ด์„œ ์ œ๊ณตํ•ด์ค€๋‹ค. ๊น”๋”ํ•˜๊ฒŒ DL ๊ธฐ๋Šฅ๋งŒ ์ œ๊ณตํ•œ๋‹ค.

 

JSR330 Provider

@Scope("singleton")
static class ClientBean {
    private Provider<PrototypeBean> prototypeBeanProvider;

    public ClientBean(Provider<PrototypeBean> prototypeBeanProvider) {
        this.prototypeBeanProvider = prototypeBeanProvider;
    }

    public int logic() {
        PrototypeBean prototypeBean = prototypeBeanProvider.get();
        prototypeBean.addCount();
        int count = prototypeBean.getCount();
        return count;
    }
}

ObjectProvider ๋Œ€์‹  ์ž๋ฐ”์—์„œ ์ œ๊ณตํ•˜๋Š” Provider๊ฐ€ ์žˆ๋‹ค.

 

ํ”„๋กœํ† ํƒ€์ž… ๋นˆ์€ ๋งค๋ฒˆ ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…์ด ์™„๋ฃŒ๋œ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•˜๋ฉด ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ํ•˜์ง€๋งŒ, ํ”„๋กœํ† ํƒ€์ž… ์ž์ฒด๋ฅผ ์‹ค๋ฌด์—์„œ๋Š” ๋ณ„๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ผ์ด ๋“œ๋ฌผ๋‹ค๊ณ  ํ•œ๋‹ค.

ObjectProvider์™€ JSR330 Provider๋Š” ํ”„๋กœํ† ํƒ€์ž… ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ๊ฒƒ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์›น ์Šค์ฝ”ํ”„

์›น ์Šค์ฝ”ํ”„๋Š” ์›น ํ™˜๊ฒฝ์—์„œ๋งŒ ๋™์ž‘ํ•œ๋‹ค.

 

์›น ์Šค์ฝ”ํ”„์˜ ์ข…๋ฅ˜

  • request : HTTP ์š”์ฒญ ํ•˜๋‚˜๊ฐ€ ๋“ค์–ด์˜ค๊ณ  ๋‚˜๊ฐˆ ๋•Œ ๊นŒ์ง€ ์œ ์ง€๋˜๋Š” ์Šค์ฝ”ํ”„, ๊ฐ๊ฐ์˜ HTTP ์š”์ฒญ๋งˆ๋‹ค ๋ณ„๋„์˜ ๋นˆ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , ๊ด€๋ฆฌ๋œ๋‹ค.
  • session : HTTP Session๊ณผ ๋™์ผํ•œ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ฐ€์ง€๋Š” ์Šค์ฝ”ํ”„
  • application : ์„œ๋ธ”๋ฆฟ ์ปจํ…์ŠคํŠธ( `ServletContext` )์™€ ๋™์ผํ•œ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ฐ€์ง€๋Š” ์Šค์ฝ”ํ”„
  • websocket : ์›น ์†Œ์ผ“๊ณผ ๋™์ผํ•œ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ฐ€์ง€๋Š” ์Šค์ฝ”ํ”„ 

HTTP ์š”์ฒญ์ด ๊ฐ™์œผ๋ฉด, ๊ฐ™์€ ์Šคํ”„๋ง ๋นˆ์„ ์ด์šฉํ•œ๋‹ค!

 

request ์Šค์ฝ”ํ”„ ์˜ˆ์ œ ๊ฐœ๋ฐœ

๋™์‹œ์— ์—ฌ๋Ÿฌ HTTP ์š”์ฒญ์ด ์˜ค๋ฉด ์ •ํ™•ํžˆ ์–ด๋–ค ์š”์ฒญ์ด ๋‚จ๊ธด ๋กœ๊ทธ์ธ์ง€ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์–ด๋ ต๋‹ค. ์ด๋Ÿด ๋•Œ request ์Šค์ฝ”ํ”„๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

๊ธฐ๋Œ€ํ•˜๋Š” ๊ณตํ†ต ํฌ๋ฉง: [UUID][requestURL] {message} -> requestURL์„ ํ˜ธ์ถœํ•˜๋ฉด message ์‹คํ–‰
UUID๋ฅผ ์‚ฌ์šฉํ•ด์„œ HTTP ์š”์ฒญ์„ ๊ตฌ๋ถ„ํ•˜์ž.
requestURL ์ •๋ณด๋„ ์ถ”๊ฐ€๋กœ ๋„ฃ์–ด์„œ ์–ด๋–ค URL์„ ์š”์ฒญํ•ด์„œ ๋‚จ์€ ๋กœ๊ทธ์ธ์ง€ ํ™•์ธํ•˜์ž.

 

์ฐธ๊ณ ) UUID = Universally Unique Identifier

 

MyLogger

package hello.core.common;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.Setter;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.UUID;

@Component
@Scope("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);
    }
}

 Request ์š”์ฒญ์ด ์˜ค๋ฉด ์ƒ์„ฑ๋˜๋Š” ์Šคํ”„๋ง ๋นˆ์ด๋‹ค.

 

Controller

package hello.core.web;

import hello.core.common.MyLogger;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@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";
    }
}

log-demo๋กœ Request๊ฐ€ ์žˆ๋‹ค๋ฉด, HttpServletRequest๋กœ ์ „๋‹ฌ๋ฐ›๊ณ , mylogger์— ์š”์ฒญ๋ฐ›์€ URL์„ ์„ค์ •ํ•œ๋‹ค.

 

์ฐธ๊ณ ) HttpServletRequest : HTTP ํ”„๋กœํ† ์ฝœ์˜ Request ์ •๋ณด๋ฅผ ์„œ๋ธ”๋ฆฟ์—๊ฒŒ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

Servlet : ์›น ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ์ž๋ฐ” ๊ธฐ๋ฐ˜์˜ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ์ˆ 

 

Service

package hello.core.web;

import hello.core.common.MyLogger;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class LogDemoService {
    private final MyLogger myLogger;

    public void logic(String id){
        myLogger.log("server id = " + id);
    }
}

ํ•˜์ง€๋งŒ, ํ˜„์žฌ ๋ฌธ์ œ๋Š” Request๊ฐ€ ์—†๋Š”๋ฐ, MyLogger์˜ scope๋Š” request์ด๋ฏ€๋กœ, Controller์™€ Service์— ์ฃผ์ž…ํ•  ์ˆ˜ ์—†์–ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค!!

 

ํ•ด๊ฒฐ

@Controller
@RequiredArgsConstructor
public class LogDemoController {
	
    ...
    @RequestMapping("log-demo")
    @ResponseBody
    public String logDemo(HttpServletRequest request){
        MyLogger myLogger = myLoggerProvider.getObject();
        ...
    }
}
@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๋ฅผ ์ด์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ–ˆ์ง€๋งŒ, ๋” ๊ฐ„๋‹จํžˆ ํ•˜๊ณ  ์‹ถ๋‹ค...

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Setter
public class MyLogger {
}

๊ฐ€์งœ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•ด์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ์ง„์งœ ๊ฐ์ฒด๋ฅผ ์ฐพ์•„์„œ ๋ฐ”๊ฟ”์น˜๊ธฐ ํ•œ ํ›„ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

๊ฐ€์งœ ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์›๋ณธ ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›์•„์„œ ๋งŒ๋“ค์–ด์กŒ๊ธฐ ๋•Œ๋ฌธ์— ์ง„์งœ ๊ฐ์ฒด์™€ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Provider์™€ ํ”„๋ก์‹œ์˜ ๊ณตํ†ต์ ์€ ์ง„์งœ ๊ฐ์ฒด ์กฐํšŒ๋ฅผ ๊ผญ ํ•„์š”ํ•œ ์‹œ์ ๊นŒ์ง€ ์ง€์—ฐ์ฒ˜๋ฆฌํ•œ๋‹ค๋Š” ์ ์ด๋‹ค.

 

์‹ฑ๊ธ€ํ†ค์ด ์•„๋‹Œ ํŠน๋ณ„ํ•œ scope๋Š” ํ•„์š”ํ•œ ๊ณณ์—์„œ๋งŒ ์‚ฌ์šฉํ•˜์ž.