#6. ์คํ๋ง ์ปจํ ์ด๋์ ์คํ๋ง ๋น
by JiwonDev์์์ ์คํ๋ง์ด ๋์ค๊ฒ๋ ๊ฐ๋ ์ ๋ํด ์์๋ณด์๋ค๋ฉด, ์ด๋ฒ ๊ธ๋ถํฐ๋ ์คํ๋ง ์์ฒด๊ฐ ์ด๋ป๊ฒ ๋์ํ๋์ง ์์๋ณด์.
*ํด๋น ๊ธ์ ๊น์ํ๋์ ์ธํ๋ฐ ๊ฐ์๋ฅผ ๊ณต๋ถํ๊ณ ์ ๋ฆฌํ ๋ด์ฉ์ ๋๋ค.
# ์คํ๋ง ์ปจํ ์ด๋
//์คํ๋ง ์ปจํ
์ด๋ ์์ฑ ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
@Configuration public class AppConfig { @Bean public MemberService memberService() { return new MemberServiceImpl(memberRepository()); } @Bean public OrderService orderService() { return new OrderServiceImpl( memberRepository(), discountPolicy()); } ... ์๋ต... }
- ApplicationContext ๋ฅผ ์คํ๋ง ์ปจํ ์ด๋๋ผ ํ๋ค.
- ApplicationContext ๋ ์ธํฐํ์ด์ค์ด๋ค.
- ์ฐธ๊ณ ๋ก ์คํ๋ง ์ปจํ ์ด๋๋ ๋ณดํต ์ด๋ ธํ ์ด์ (@) ๊ธฐ๋ฐ์ ์๋ฐ ํด๋์ค๋ก ๋ง๋ค์ง๋ง, ๊ธฐ์กด์ ์๋ธ๋ฆฟ์ ์ค์ ํ๋ ๊ฒ์ฒ๋ผ XML ๊ธฐ๋ฐ์ผ๋ก๋ ๋ง๋ค ์ ์๋ค.
- ์๋ฐ ์ค์ ํด๋์ค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์คํ๋ง ์ปจํ
์ด๋(ApplicationContext)๋ฅผ ๋ง๋ค์ด๋ณด์.
- new AnnotationConfigApplicationContext(appConfig.class)
- ์ด ํด๋์ค๋ ApplicationContext ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด์ด๋ค.
์ฐธ๊ณ ๋ก ์ ํํ๋ ์คํ๋ง ์ปจํ ์ด๋๋ฅผ ๋ถ๋ฅผ ๋ ์ต์์์ BeanFactory์ ์์์ธ ApplicationContext๋ฅผ ๊ตฌ๋ถํด์ ์ฌ์ฉํ์ง๋ง, ์ด๋ ๋์ค์ ์์๋ณด๋๋ก ํ์. (๋ณดํต BeanFactory๋ฅผ ์ง์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ๊ฑฐ์ ์๊ณ , AppContext๋ฅผ ์คํ๋ง ์ปจํ ์ด๋๋ผ๊ณ ํ๋ค.)
# ์คํ๋ง ์ปจํ ์ด๋์ ์์ฑ๊ณผ์
@1 ์ปจํ ์ด๋ ์์ฑ
ApplicationContext๋ผ๋ ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด๋ฅผ ๋ฑ๋กํด์ค์ ์ฌ์ฉํ๋ค. ์คํ๋ง ์ปจํ ์ด๋๋ฅผ ์์ฑ ํ ๋์๋ ๊ตฌ์ฑ ์ ๋ณด๋ฅผ ๊ตฌํ์ฒด์ ์ง์ ํด์ฃผ์ด์ผ ํ๋๋ฐ, ์ฌ๊ธฐ์์๋ AppConfig.class๋ฅผ ์ด์ฉํ์ฌ [์๋ฐ์ Class ๊ฐ์ฒด]๋ฅผ ํตํด ๊ตฌ์ฑ์ ๋ณด๋ฅผ ์ง์ ํ์๋ค. Class ๊ฐ์ฒด๊ฐ ๋ญ์ฃ ?
์๋ฐ๋ Object.class ๋ฅผ ํตํด ํด๋น ๊ฐ์ฒด์ Class ๊ฐ์ฒด๋ฅผ ๋ฐ์ ์ ์๋ค.
Class.forName("ํจํค์ง ์ ์ฒด ๊ฒฝ๋ก") ํด๋์ค์ด๋ฆ.class MyHello.class Class.forName("mypackage.core.MyHello")
Class ํด๋์ค๋ ์๋ฐ์์ ์ฌ์ฉ๋๋ ํด๋์ค์ ๋ํ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ , ์ด๋ฅผ ์ ์ฅํ์ฌ ๋๊ฒจ์ค ์ ์๋ ๊ฐ์ฒด์ด๋ค.์ฆ ์ฝ๊ฒ๋งํด [ํด๋์ค์ ๊ตฌ์กฐ] ์์ฒด๋ฅผ ํ๋์ ํด๋์ค๋ก ํํํด๋์ ๊ฐ์ฒด๋ผ๋ ๋ง์ด๋ค.
์ฐธ๊ณ ๋ก ์ด๋ ๊ฒ ๊ฐ์ฒด๋ฅผ ํตํด [๋ค๋ฅธ ๊ฐ์ฒด]์ ์ ๋ณด๋ฅผ ๋ถ์ํ๋ ๋ฐฉ์์ Reflection ๊ธฐ๋ฒ์ด๋ผ๊ณ ํ๋ค. ํด๋์ค ํ์ผ์ ์์น๋ ์ด๋ฆ๋ง์ผ๋ก๋ ํด๋น ํด๋์ค์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์๊ฒ(Hello.class) ๋ง๋ค์ด์ฃผ๋ ์ ์ฐํ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ด๋ค. ์คํ๋ง ํ๋ ์์ํฌ์์๋ ๋ฆฌํ๋ ์ ๊ธฐ๋ฒ์ ์์ฃผ ๋ง์ด ์ฌ์ฉ์๋ ๊ฑธ ๋ณผ ์ ์๋ค.
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

@2 ์คํ๋ง ๋น ๋ฑ๋ก
์ปจํ ์ด๋์ ํ๋ผ๋ฉํ๋ก ๋๊ฒจ์ค ๊ฐ์ฒด(AppConfig.class)์์ @Bean ์ผ๋ก ๋ฑ๋ก๋ ๋ฉ์๋๋ฅผ ์ ๋ถ ํธ์ถํ์ฌ ๋ฐํ ๊ฐ์ ์คํ๋ง ๋น ์ ์ฅ์์ ๋ชจ์๋๋ค. ๋น ์ด๋ฆ์ ๋ฉ์๋ ๋ช ์ผ๋ก ์ง์ ๋๋ฉฐ ์ํ๋ค๋ฉด ๋ฐ๊ฟ ์ ์๊ธดํ๋ค. @Bean(name="...") ์ฐธ๊ณ ๋ก ๋น์ ์ด๋ฆ์ ํญ์ ๋ค๋ฅด๊ฒ ๋ถ์ฌํด์ฃผ์ด์ผํ๋๋ฐ, ๊ฐ์ ์ด๋ฆ์ ๋ถ์ฌํ๋ฉด ๋น ์ ์ฅ์์์๋ ๊ธฐ์กด์ ๋น์ ๋ฎ์ด๋ฒ๋ฆฌ๊ฑฐ๋ ๋ฌด์๋์ด ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. (* ์์ฆ์ ์คํ๋ง ๋ถํธ๊ฐ ์ด๋ฐ ์ค์ ์ค์๋ฅผ ์ค๋ฅ๋ก ๊ฐ๋ฐ์์๊ฒ ์๋ ค์ค๋ค. )

@3 ์คํ๋ง ๋น ์์กด๊ด๊ณ ์ค์
์ ์ฅ์์ ๋ฑ๋ก๋ ๋น๋ค์ ์์กด๊ด๊ณ๋ฅผ ์ฃผ์ ํ๋ค. ์ค์ ์คํ๋ง ๋น์์ ์์กด๊ด๊ณ์ธ ๊ฐ์ฒด์ ์ฐธ์กฐ ๊ฐ๋ค์ด ์ฐ๊ฒฐ๋๋ค. ์ฐ๊ฒฐ์ด๋ผ๋๊ฒ ๋จ์ํ ์๋ฐ ๋ฉ์๋๋ฅผ ํธ์ถํด์ ์์ฑํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ผ ์ ์์ง๋ง, ์ด์๋ ์ฐจ์ด๊ฐ ์๋ค. (*์ฑ๊ธํค ์ปจํ ์ด๋)


์ด์ฒ๋ผ ์คํ๋ง์ ๋น์ ์์ฑํ๋ ๋จ๊ณ์ ์์กด๊ด๊ณ๋ฅผ ์ฃผ์ ํ๋ ๋จ๊ณ๊ฐ ๋๋์ด์ ธ ์๋ค. ํ์ง๋ง ์์ ์์ ์ฒ๋ผ ์๋ฐ ์ฝ๋๋ก ์คํ๋ง๋น์ ๋ฑ๋กํ๋ฉด ์์ฑ์๋ฅผ ํธ์ถํ๋ฉด์ ์์กด๊ด๊ณ ์ฃผ์ ๋ ํ๋ฒ์ ์ฒ๋ฆฌ๋๋๋ฐ, ์ด๋ ๋์ค์ '์์กด๊ด๊ณ ์๋ ์ฃผ์ '์ ๋ฐฐ์ฐ๋ฉด์ ์์๋ณด๋๋ก ํ์.
# ์ปจํ ์ด๋์ ๋ฑ๋ก๋ ์คํ๋ง ๋น ์กฐํํ๊ธฐ
ํ ์คํธ ์ฝ๋(JUnit5)๋ฅผ ์ด์ฉํด ์คํ๋ง ์ปจํ ์ด๋์ ๋ฑ๋ก๋ ๋ฐ์ดํฐ๋ฅผ ํ์ธํด๋ณด์. ์ฐธ๊ณ ๋ก ๋ฑ๋ก๋ ๋น๋ค์ ์ด๋ฆ์ AppContext ๊ฐ์ฒด์ ๋ฉ์๋๋ก ์ป์ด์ฌ ์ ์๋ค. ๋ด๊ฐ ๋ฑ๋กํ ๋น ์ด์ธ์ ์คํ๋ง์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉํ๋ ๋น๋ค๋ ์์์ ์ ์ํ์.
class ApplicationContextInfoTest { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); @Test @DisplayName("๋ชจ๋ ๋น ์ถ๋ ฅํ๊ธฐ") void findAllBean() { String[] beanDefinitionNames = ac.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { Object bean = ac.getBean(beanDefinitionName); System.out.println("name=" + beanDefinitionName + " object=" + bean); } } }

๋ํ getBeanDefinition์ ๋น๊ตํด์ ๋ด๊ฐ ๋ฑ๋กํ ์ดํ๋ฆฌ์ผ์ด์ ๋น(appConfig, memberService...)๋ง ํ์ธํ ์๋ ์๋ค.
import org.springframework.beans.factory.config.BeanDefinition; @Test @DisplayName("์ ํ๋ฆฌ์ผ์ด์
๋น ์ถ๋ ฅํ๊ธฐ") void findApplicationBean() { String[] beanDefinitionNames = ac.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName); //Role ROLE_APPLICATION: ์ง์ ๋ฑ๋กํ ์ ํ๋ฆฌ์ผ์ด์
๋น //Role ROLE_INFRASTRUCTURE: ์คํ๋ง์ด ๋ด๋ถ์์ ์ฌ์ฉํ๋ ๋น if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) { Object bean = ac.getBean(beanDefinitionName); System.out.println("name=" + beanDefinitionName + " object=" + bean); } } }
@1 Bean ์กฐํ - ๊ธฐ๋ณธ
์์ ํ ์คํธ ์ผ์ด์ค๋ฅผ ๋ณด๋ฉด ์ ์ ์์๋ง, ๊ธฐ๋ณธ์ ์ผ๋ก ๋น์ AppContext์ ์๋ ๋ฉ์๋๋ก ์กฐํ ํ ์ ์๋ค. ๋ง์ฝ ํด๋น ๋น์ด ์กด์ฌํ์ง ์๋๋ค๋ฉด NoSuchBeanDefinitionException ๊ฐ ๋ฐ์ํ๋ค.
- getBean("Bean ์ด๋ฆ", ํ์ )
- getBean(ํ์ )
import org.junit.jupiter.api.Assertions; // Assertions๊ฐ ๊ฐ์ ์ด๋ฆ์ด 2๊ฐ์์ ์ ์ํ์. import static org.assertj.core.api.Assertions.*; class ApplicationContextBasicFindTest { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); @Test @DisplayName("๋น ์ด๋ฆ์ผ๋ก ์กฐํ") void findBeanByName() { MemberService memberService = ac.getBean("memberService", MemberService.class); assertThat(memberService).isInstanceOf(MemberServiceImpl.class); } @Test @DisplayName("์ด๋ฆ ์์ด ํ์
๋ง์ผ๋ก ์กฐํ") void findBeanByType() { MemberService memberService = ac.getBean(MemberService.class); assertThat(memberService).isInstanceOf(MemberServiceImpl.class); } @Test @DisplayName("๊ตฌ์ฒด ํ์
์ผ๋ก ์กฐํ") void findBeanByName2() { MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class); assertThat(memberService).isInstanceOf(MemberServiceImpl.class); } @Test @DisplayName("๋น ์ด๋ฆ์ผ๋ก ์กฐํ์คํจ") void findBeanByNameX() { //ac.getBean("xxxxx", MemberService.class); ํ
์คํธ Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> ac.getBean("xxxxx", MemberService.class)); } }
* ํ ์คํธ ์ฝ๋๋ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ญํ ๊ณผ ๊ตฌํ์ ๋ถ๋ฆฌํ๋๊ฒ ์ข๋ค. ์์ ์์ ์ฒ๋ผ ์คํ๋ง ๋น์ ์กฐํํ ๋ ๊ตฌํ์ฒด๋ฅผ ์ด์ฉํด ๊ตฌ์ฒด์ ์ผ๋ก ์กฐํํ๋ฉด ์ ์ฐ์ฑ์ด ๋จ์ด์ง์ ์ ์ํ์.
@2 Bean ์กฐํ - ๋์ผํ ํ์ ์ด 2๊ฐ ์ด์์ธ ๊ฒฝ์ฐ
getBean์ ์ด์ฉํด ํ์ ์ผ๋ก ๋น์ ์กฐํํ ์ ์๋ค ํ๋๋ฐ, ๋ง์ฝ ํ์ ์ด ๊ฐ๋ค๋ฉด ์ด๋ป๊ฒ ์กฐํํ ์ ์์๊น? ์ด๋ ac.getBeansOfType()์ ์ด์ฉํ์ฌ ํด๋น ํ์ ์ ๋ชจ๋ ๋น์ ์กฐํํ ์ ์๋ค.
* ํ ์คํธ๋ฅผ ํธํ๊ฒ ํ๊ธฐ์ํด ์ฝ๋์์ ๊ฐ๋จํ๊ฒ ํ ์คํธ์ฉ AppConfig ๊ฐ์ฒด(SameBeanConfig)๋ฅผ ๋ง๋ค์๋ค.
class ApplicationContextSameBeanFindTest { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class); @Test @DisplayName("ํ์
์ผ๋ก ์กฐํ์ ๊ฐ์ ํ์
์ด ๋ ์ด์ ์์ผ๋ฉด, ์ค๋ณต ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค") void findBeanByTypeDuplicate() { //DiscountPolicy bean = ac.getBean(MemberRepository.class); assertThrows(NoUniqueBeanDefinitionException.class, () -> ac.getBean(MemberRepository.class)); } @Test @DisplayName("ํ์
์ผ๋ก ์กฐํ์ ๊ฐ์ ํ์
์ด ๋ ์ด์ ์์ผ๋ฉด, ๋น ์ด๋ฆ์ ์ง์ ํ๋ฉด ๋๋ค") void findBeanByName() { MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class); assertThat(memberRepository).isInstanceOf(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); // ๊ฐ์ ํ์
์ 2๊ฐ์ฌ์ผ ํ๋ค. assertThat(beansOfType.size()).isEqualTo(2); } // ํ
์คํธ์ฉ AppConfig ์ฌ์ฉ. ํ
์คํธ๋ผ์ ๋์ถฉ ๋ง๋ค์๋ค. @Configuration static class SameBeanConfig { @Bean public MemberRepository memberRepository1() { return new MemoryMemberRepository(); } @Bean public MemberRepository memberRepository2() { return new MemoryMemberRepository(); } } }

@3 Bean ์กฐํ - ์์๊ด๊ณ
๋ถ๋ชจ ํ์ ์ผ๋ก ์กฐํํ๋ฉด, ์์ ํ์ ๋ ํจ๊ป ์กฐํํ๋ค. ์ฆ Object๋ก ์กฐํํ๋ฉด ๋ชจ๋ ์คํ๋ง ๋น์ด ๋์ค๊ฒ ๋๋ค. ์ค๋ณต์ ํ์ธํ ๋ ์์๊ด๊ณ๋ฅผ ์ ์ํ์ฌ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋๋ก ํ์.

class ApplicationContextExtendsFindTest { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class); @Test @DisplayName("๋ถ๋ชจ ํ์
์ผ๋ก ์กฐํ์, ์์์ด ๋ ์ด์ ์์ผ๋ฉด, ์ค๋ณต ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค") void findBeanByParentTypeDuplicate() { //DiscountPolicy bean = ac.getBean(DiscountPolicy.class); assertThrows(NoUniqueBeanDefinitionException.class, () -> ac.getBean(DiscountPolicy.class)); } @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)); } } @Test @DisplayName("๋ถ๋ชจ ํ์
์ผ๋ก ๋ชจ๋ ์กฐํํ๊ธฐ - Object") void findAllBeanByObjectType() { Map<String, Object> beansOfType = ac.getBeansOfType(Object.class); for (String key : beansOfType.keySet()) { System.out.println("key = " + key + " value=" + beansOfType.get(key)); } } @Configuration static class TestConfig { @Bean public DiscountPolicy rateDiscountPolicy() { return new RateDiscountPolicy(); } @Bean public DiscountPolicy fixDiscountPolicy() { return new FixDiscountPolicy(); } } }
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev