Java/Spring

[Spring] μŠ€ν”„λ§ 핡심 원리 기본편 (2)

벼리01 2023. 12. 27. 22:42

 

기쑴의 μ½”λ“œλ₯Ό μŠ€ν”„λ§μœΌλ‘œ μ „ν™˜ν•œλ‹€.

 

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository());
    }

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

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public DiscountPolicy discountPolicy(){
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }

}

 

public class MemberApp {
    public static void main(String[] args) {

//        AppConfig appConfig = new AppConfig();
//        MemberService memberService = new MemberServiceImpl();
//        MemberService memberService = appConfig.memberService();

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
        .
        .
        .

 

 

 

기쑴에 μ‚¬μš©ν•˜λ˜ `AppConfig`에 μ• λ„ˆν…Œμ΄μ…˜μ„ μΆ”κ°€ν•΄μ„œ μŠ€ν”„λ§μœΌλ‘œ μ‚¬μš©ν•  수 μžˆλ„λ‘ λ³€κ²½ν•œλ‹€.

`ApplicationContext`λŠ” μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλΌκ³  ν•œλ‹€. `@Bean`을 λͺ¨λ‘ 관리해쀀닀. 

κΈ°μ‘΄μ—λŠ” κ°œλ°œμžκ°€ `AppConfig`둜 직접 κ΄€λ¦¬ν–ˆμ§€λ§Œ 이제 μ• λ„ˆν…Œμ΄μ…˜μ„ μž‘μ„±ν•˜μ—¬ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλ₯Ό 톡해 μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•œλ‹€.

μŠ€ν”„λ§ 빈(`@Bean` μ• λ„ˆν…Œμ΄μ…˜μ΄ 뢙은 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•΄μ„œ λ°˜ν™˜λœ 객체)은 `applicationContext.getBean("λ©”μ„œλ“œμ΄λ¦„", ν΄λž˜μŠ€μ΄λ¦„.class)` λ©”μ„œλ“œ 톡해 찾을 수 μžˆλ‹€.

`AppConfig`λŠ” `@Configuration` μ• λ„ˆν…Œμ΄μ…˜μ΄ 뢙은 클래슀. μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ DIλ₯Ό ν•  수 μžˆλ„λ‘ ν•΄μ€€λ‹€.

 

 

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ μƒμ„±λ˜λŠ” κ³Όμ •

 

ApplicationContext applicationContext = 
	new AnnotationConfigApplicationContext(AppConfig.class);

 

`ApplicationContext`λŠ” μΈν„°νŽ˜μ΄μŠ€. `AnnotationConfigApplicationContext`λŠ” `ApplicationContext`의 κ΅¬ν˜„μ²΄μ΄λ‹€.

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” xml둜 λ§Œλ“€ μˆ˜λ„ 있고, μ• λ„ˆν…Œμ΄μ…˜ 기반의 μžλ°” 클래슀 μ„€μ •μœΌλ‘œ λ§Œλ“€ 수 μžˆλ‹€.(ν›„μžκ°€ νŽΈλ¦¬ν•΄μ„œ 더 많이 μ‚¬μš©ν•œλ‹€.)

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλ₯Ό 생성할 λ•ŒλŠ” ꡬ성 정보λ₯Ό μ§€μ •ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€. μ—¬κΈ°μ„œλŠ” `AppConfig`λ₯Ό μ‚¬μš©ν–ˆλ‹€.

 

μŠ€ν”„λ§ 빈 등둝

`@Bean` μ• λ„ˆν…Œμ΄μ…˜μ΄ 뢙은 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ λ°˜ν™˜λ°›μ€ 객체λ₯Ό μŠ€ν”„λ§ 빈으둜 λ“±λ‘ν•œλ‹€. 이름은 `getBean()`μ—μ„œ 인자둜 전달 받은 String으둜, κ°’μœΌλ‘œλŠ” ν•΄λ‹Ή 객체λ₯Ό κ°€μ§„λ‹€. 

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” μ„€μ • 정보λ₯Ό μ°Έκ³ ν•΄μ„œ 의쑴 관계λ₯Ό μ£Όμž…(DI)ν•œλ‹€. `@Configuration` μ• λ„ˆν…Œμ΄μ…˜μ„ 달고 μžˆλŠ” `AppConfig`μ—μ„œ `memberService`κ°€ `memberRepository`λ₯Ό μ˜μ‘΄ν•˜κ³ , `orderService`κ°€ `memberRepository`와 `discountPolicy`λ₯Ό μ˜μ‘΄ν•˜κ³  있기 λ•Œλ¬Έμ— 이λ₯Ό μ°Έκ³ ν•˜μ—¬ 의쑴 관계λ₯Ό μ£Όμž…ν•΄μ€€λ‹€.

 

 

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— λ“±λ‘λœ λͺ¨λ“  빈 쑰회

String[] beanNames = getBeanDefinitionNames();

iter + tab (ν–₯μƒλœ forλ¬Έ μžλ™ 생성)

soutv System.out.println("name" + name); μžλ™ 생성

 

    @Test
    @DisplayName("λͺ¨λ“  빈 좜λ ₯ν•˜κΈ°")
    void findApplicationBean(){
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

//            μŠ€ν”„λ§μ΄ μ‚¬μš©ν•˜λŠ” λ‚΄λΆ€ 빈 말고 λ‚΄κ°€ λ“±λ‘ν•œ μŠ€ν”„λ§ 빈
//            BeanDefinition.ROLE_APPLICATION
            if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
                Object bean = ac.getBean(beanDefinitionName);
                System.out.println("name " + beanDefinitionName + " bean = " + bean);

            }

        }
    }
    
//	name = appConfig object = hello.core.order.AppConfig$$SpringCGLIB$$0@46b61c56
//	name = memberService object = hello.core.member.MemberServiceImpl@2e48362c
//	name = memberRepository object = hello.core.member.MemoryMemberRepository@1efe439d
//	name = orderService object = hello.core.order.OrderServiceImpl@be68757
//	name = discountPolicy object = hello.core.discount.RateDiscountPolicy@7d446ed1

 

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

    @Test
    @DisplayName("빈 μ΄λ¦„μœΌλ‘œ 쑰회")
    void findBeanByName() {
        MemberService memberService = ac.getBean("memberService", MemberService.class);
//        System.out.println("memberService = " + memberService);
//        System.out.println("memberService.getClass() = " + memberService.getClass());

        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

    @Test
    @DisplayName("이름 없이 νƒ€μž…μœΌλ‘œλ§Œ 쑰회")
    void findBeanByType() {
        MemberService memberService = ac.getBean(MemberService.class);
//        System.out.println("memberService = " + memberService);
//        System.out.println("memberService.getClass() = " + memberService.getClass());

        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

    @Test
    @DisplayName("ꡬ체 νƒ€μž…μœΌλ‘œ 쑰회")
    void findBeanByType2() {
//        쒋은 μ½”λ“œλŠ” μ•„λ‹ˆλ‹€.
//        μ—­ν• (좔상화)에 μ˜μ‘΄ν•΄μ•Όμ§€ ꡬ체 ν΄λž˜μŠ€μ— μ˜μ‘΄ν•΄μ„œλŠ” μ•ˆ λœλ‹€.
        MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
//        System.out.println("memberService = " + memberService);
//        System.out.println("memberService.getClass() = " + memberService.getClass());


        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

    @Test
    @DisplayName("빈 μ΄λ¦„μœΌλ‘œ 쑰회 X")
    void findBeanByNameX(){
        MemberService X = ac.getBean("X", MemberService.class);
//        였λ₯Έμͺ½μ— μžˆλŠ” λžŒλ‹€μ‹μ„ μ‹€ν–‰ν•˜λ©΄ μ™Όμͺ½μ— μžˆλŠ” μ˜ˆμ™Έκ°€ 터져야함
        org.junit.jupiter.api.Assertions.assertThrows(NoSuchBeanDefinitionException.class,
                () -> ac.getBean("X", MemberService.class));
    }
}

 

 

Testμ—μ„œ 검증은 항상 assertThat으둜 ν•œλ‹€!

 

 

public class ApplicationContextSameBeanFindTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

    @Test
    @DisplayName("νƒ€μž…μœΌλ‘œ 쑰회 μ‹œ 같은 νƒ€μž…μ΄ λ‘˜ 이상 있으면 쀑볡 였λ₯˜κ°€ λ°œμƒν•œλ‹€.")
    void findBeanByTypeDuplicate() {
//        NoUniqueBeanDefinitionException
//        available: expected single matching bean but found 2: memberRepository,memberRepository2
//        빈이 두 κ°œμž„
        Assertions.assertThrows(NoUniqueBeanDefinitionException.class, () -> ac.getBean(MemberRepository.class));
    }

    @Test
    @DisplayName("νƒ€μž…μœΌλ‘œ 쑰회 μ‹œ 같은 νƒ€μž…μ΄ λ‘˜ 이상 있으면 빈 이름을 μ§€μ •ν•˜λ©΄ λœλ‹€")
    void findBeanByName() {
        MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
        org.assertj.core.api.Assertions.assertThat(memberRepository).isInstanceOf(MemberRepository.class);
    }

    @Test
    @DisplayName("νŠΉμ • νƒ€μž…μ„ λͺ¨λ‘ μ‘°νšŒν•˜κΈ°")
    void findAllBeanByName() {
        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);
        org.assertj.core.api.Assertions.assertThat(beansOfType.size()).isEqualTo(2);
    }


    @Configuration
    static class SameBeanConfig {
        @Bean
        public MemberRepository memberRepository() {
            return new MemoryMemberRepository();
        }

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


}

 

 

μŠ€ν”„λ§ 빈 쑰회- 상속 관계

λΉˆμ€ λΆ€λͺ¨ νƒ€μž…μœΌλ‘œ μ‘°νšŒν•˜λ©΄ μžμ‹ νƒ€μž…λ„ ν•¨κ»˜ μ‘°νšŒν•œλ‹€. 졜고 쑰상인 `Object`둜 μ‘°νšŒν•  경우 λͺ¨λ“  μŠ€ν”„λ§ λΉˆμ„ μ‘°νšŒν•œλ‹€.

 

	@Test
    @DisplayName("λΆ€λͺ¨ νƒ€μž…μœΌλ‘œ λͺ¨λ‘ μ‘°νšŒν•˜κΈ°")
    void findAllBeanByParentType() {

        Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);

        for (String key : beansOfType.keySet()) {
            System.out.println("key= " + key + " bean = " + 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 + " bean = " + beansOfType.get(key));
        }
    }

 

iter (ν–₯μƒλœ forλ¬Έ)

soutv (for문의 μš”μ†Œ String + μš”μ†Œ)둜 좜λ ₯λ¬Έ λ§Œλ“€μ–΄μ€Œ 

 

 

`BeanFactory`

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ˜ 졜고 쑰상 μΈν„°νŽ˜μ΄μŠ€

μŠ€ν”„λ§ λΉˆμ„ κ΄€λ¦¬ν•˜κ³  μ‘°νšŒν•˜λŠ” μ—­ν• . `getBean()`을 μ œκ³΅ν•œλ‹€. (쑰회, 관리. μš°λ¦¬κ°€ μ‚¬μš©ν•˜λŠ” λŒ€λΆ€λΆ„μ˜ κΈ°λŠ₯은 `BeanFactory`κ°€ μ œκ³΅ν•˜λŠ” κΈ°λŠ₯이닀. 직접 μ‚¬μš©ν•  일은 거의 μ—†λ‹€. λΆ€κ°€ κΈ°λŠ₯이 ν¬ν•¨λœ `ApplicationContext`λ₯Ό μ‚¬μš©ν•œλ‹€.

 

`ApplicationContext`

`BeanFactory`λ₯Ό 상속 λ°›λŠ”λ‹€. μ‘°νšŒν•˜κ³  κ΄€λ¦¬ν•˜λŠ” κΈ°λŠ₯ 외에 λ§Žμ€ λΆ€κ°€ κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€.

1. λ©”μ‹œμ§€ μ†ŒμŠ€λ₯Ό ν™œμš©ν•œ κ΅­μ œν™” κΈ°λŠ₯. ex) ν•œκ΅­μ—μ„œ λ“€μ–΄μ˜€λ©΄ ν•œκ΅­μ–΄λ‘œ, μ˜μ–΄κΆŒμ—μ„œ λ“€μ–΄μ˜€λ©΄ μ˜μ–΄λ‘œ 좜λ ₯

2. ν™˜κ²½ λ³€μˆ˜. 둜컬 개발(λ‚΄ pcμ—μ„œ 개발) ν…ŒμŠ€νŠΈ μ„œλ²„μ—μ„œ 개발, μ‹€μ œ κ΅¬λ™λ˜λŠ” 운영 ν™˜κ²½μ„ κ΅¬λΆ„ν•΄μ„œ μ²˜λ¦¬ν•΄μ€€λ‹€.

3. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 이벀트 처리

4. λ¦¬μ†ŒμŠ€ 쑰회. 파일, 클래슀패슀, μ™ΈλΆ€ λ“±μ—μ„œ λ¦¬μ†ŒμŠ€λ₯Ό μΆ”μƒν™”ν•΄μ„œ νŽΈλ¦¬ν•˜κ²Œ μ‘°νšŒν•  수 μžˆλŠ” κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€.

 

 


 

 

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” λ‹€μ–‘ν•œ ν˜•μ‹μ˜ μ„€μ • 정보λ₯Ό 받아듀일 수 μžˆλ„λ‘ μ„€κ³„λ˜μ–΄ μžˆλ‹€.

μžλ°” μ½”λ“œ, XML, Groovy λ“±

 

1. μ• λ„ˆν…Œμ΄μ…˜ 기반 μžλ°” μ½”λ“œ μ„€μ • μ‚¬μš©

`AnnotationConfigApplicationContext(AppConfig.class)` λ₯Ό μ‚¬μš©ν•˜λ©΄μ„œ μžλ°” μ½”λ“œλ‘œ 된 μ„€μ • 정보λ₯Ό λ„˜κΈ°λ©΄ λœλ‹€.

2. XML μ„€μ • μ‚¬μš©

μŠ€ν”„λ§λΆ€νŠΈμ—μ„œλŠ” XML을 μ“°μ§€ μ•ŠμœΌλ‚˜ λ ˆκ±°μ‹œ ν”„λ‘œμ νŠΈμ—μ„œλŠ” 아직 XML을 많이 μ‚¬μš©ν•œλ‹€. 컴파일 없이 빈 μ„€μ • 정보λ₯Ό λ„˜κΈΈ 수 μžˆλ‹€. 

 

μžλ°” μ½”λ“œκ°€ μ•„λ‹Œ 것은 μ „λΆ€ resources 

 

<?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>

 

public class XmlAppContext {

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

    }
}

 

 

@Configuration
public class AppConfig {

	/* <bean id="memberService" class="hello.core.member.MemberServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository"/>
    </bean>
    */
    @Bean
    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository());
    }


//	<bean id="memberRepository" class="hello.core.member.MemoryMemberRepository"/>
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

	/*	<bean id="orderService" class="hello.core.order.OrderServiceImpl">
         	<constructor-arg name="memberRepository" ref="memberRepository"/>
        	<constructor-arg name="discountPolicy" ref="discountPolicy"/>
    	</bean>
    */
    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

//	<bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy"/>
    @Bean
    public DiscountPolicy discountPolicy(){
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }

}

 

μ• λ„ˆν…Œμ΄μ…˜μœΌλ‘œ λΉˆμ„ λ“±λ‘ν•˜λŠ” 것과 μ„€μ • 정보가 μΌμΉ˜ν•œλ‹€.

 

μŠ€ν”„λ§ 빈 μ„€μ • 메타 정보 `BeanDefinition` μΈν„°νŽ˜μ΄μŠ€

μ—­ν• κ³Ό κ΅¬ν˜„μ„ κ°œλ…μ μœΌλ‘œ λ‚˜λˆˆ 것.

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” μžλ°” μ½”λ“œ `AppConfig.class` 인지 xml  `appConfig.xml`인지 λͺ°λΌλ„ λœλ‹€. `BeanDefinition`만 μ•Œλ©΄ λœλ‹€! (좔상화에 의쑴)