ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring]IoC, DI, 컨테이너 그리고 AppConfig 스프링으로 변환하기, 스프링 빈 조회하기
    Back-end/Spring 2022. 3. 4. 14:23

    안녕하세요 이번 포스팅은 지난 포스팅에 이어서 작성하겠습니다.

     

    IoC(Inversion Of Control) 제어의 역전

    기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 new로 생성하고 연결하고 실행했습니다. 한마디로 구현 객체가 프로그램의 제어 흐름을 스스로 조종했습니다. 개발자 입장에서는 자연스러운 흐름이죠.

    반면에 AppConfig가 등장한 이후에 구현 객체는 자신의 로직을 실행하는 역할만 담당합니다. 프로그램의 제어 흐름은 이제 AppConfig가 가져가게 되는 것이죠. 예를 들어서 OrderServiceImpl 은 필요한 인터페이스들을 호출하지만 어떤 구현 객체들이 실행될지 모릅니다.

     

    프로그램에 대한 제어 흐름에 대한 권한은 모두 AppConfig가 가지고 있고 심지어 OrderServiceImpl도 AppConfig가 생성합니다. 그리고 AppConfig는 OrderServiceIml이 아닌 OrderService 인터페이스의 다른 구현 객체를 생성하고 실행할 수 도있습니다. 그런 사실도 모른 채 OrderServiceImpl은 자신의 로직을 수행할 뿐입니다.

     

    이렇듯 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전이라고 합니다.

     

    프레임워크 vs 라이브러리

    - 프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 그것을 프레임워크가 맞습니다(JUnit)

     

    - 반면에 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 그것을 프레임워크가 아니라 라이브러리입니다.

     

     

     의존관계 주입 DI

    OrderServiceImpl은 DiscountPolicy 인터페이스에 의존하기 때문에 실제 어떤 구현 객체가 사용될지는 모릅니다.

    의존 관계는 정적인 클래스 의존 고나계와, 실행 시점에 결정되는 동적인 객체(인스턴스) 의존 관계 둘을 분리해서 생각해야 합니다.

     

    1. 정적인 클래스 의존관계

     

    클래스가 사용하는 import 코드만 보고 의존관계를 쉽게 판단할 수 있습니다. 정적인 의존관계는 애플리케이션을 실행하지 않아도 분석할 수 있습니다. 클래스 다이어그램을 보면 OrderServiceImpl 은 MemberRepository , DiscountPolicy에 의존한다는 것을 알 수 있습니다. 그런데 이러한 클래스 의존관계 만으로는 실제 어떤 객체가 OrderServiceImpl에 주입될지 알 수 없습니다.

     

     

     

    2. 동적인 객체 인스턴스 의존 관계

     

    애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것을 의존관계 주입이라고 합니다.

    의존관계 주입을 사용하면 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있습니다.

     

    정리
    • AppConfig처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 IoC 컨테이너 또는 DI 컨테이너라 합니다.
    • 의존관계 주입에 초점을 맞추어 최근에는 주로 DI 컨테이너라고 합니다.
    • 또는 어셈블러, 오브젝트 팩토리 등으로 불리기도 합니다.

     

    스프링으로 전환하기

     

    지금까지는 순수한 자바 코드만으로 DI를 적용했습니다. 이제 스프링을 사용해볼까요?

     

    우선 AppConfig를 다음과 같이 변경해줍니다. 코드를 전부 보여드리고 나서 설명을 하겠습니다.

    import hello.core.discount.DiscountPolicy;
    import hello.core.discount.RateDiscountPolicy;
    import hello.core.member.MemberRepository;
    import hello.core.member.MemberService;
    import hello.core.member.MemberServiceImpl;
    import hello.core.member.MemoryMemberRepository;
    import hello.core.order.OrderService;
    import hello.core.order.OrderServiceImpl;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class AppConfig {
    
     @Bean
     public MemberService memberService() {
     	return new MemberServiceImpl(memberRepository());
     }
     
     @Bean
     public OrderService orderService() {
         return new OrderServiceImpl(
             memberRepository(),
             discountPolicy());
     }
     
     @Bean
     public MemberRepository memberRepository() {
     	return new MemoryMemberRepository();
     }
     
     @Bean
     public DiscountPolicy discountPolicy() {
         return new RateDiscountPolicy();
         }
    }​

    위와 같이 변경하면 스프링 컨테이너에 스프링 빈으로 등록됩니다.

     

    다음은 MemberApp(실행, 테스트 클래스)을 변경한 코드입니다.

    import hello.core.member.Grade;
    import hello.core.member.Member;
    import hello.core.member.MemberService;
    import org.springframework.context.ApplicationContext;
    import
    org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class MemberApp {
    	 public static void main(String[] args) {
            // AppConfig appConfig = new AppConfig();
            // MemberService memberService = appConfig.memberService();
            
             ApplicationContext applicationContext = new
            		AnnotationConfigApplicationContext(AppConfig.class);
                    
             MemberService memberService =
            		applicationContext.getBean("memberService", MemberService.class);
                    
             Member member = new Member(1L, "memberA", Grade.VIP);
             memberService.join(member);
             Member findMember = memberService.findMember(1L);
             
             System.out.println("new member = " + member.getName());
             System.out.println("find Member = " + findMember.getName());
         }
    }

    AppConfig 객체를 생성해서 생성자 주입을 하는 것이 아니라 ApplicationContext에 AppConfig 클래스를 등록하고 빈을 꺼내옵니다.

     

    OrderApp은 다음과 같습니다.

    import hello.core.member.Grade;
    import hello.core.member.Member;
    import hello.core.member.MemberService;
    import hello.core.order.Order;
    import hello.core.order.OrderService;
    import org.springframework.context.ApplicationContext;
    import
    org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class OrderApp {
         public static void main(String[] args) {
            // AppConfig appConfig = new AppConfig();
            // MemberService memberService = appConfig.memberService();
            // OrderService orderService = appConfig.orderService();
            
             ApplicationContext applicationContext = new
            		AnnotationConfigApplicationContext(AppConfig.class);
             MemberService memberService =
            		applicationContext.getBean("memberService", MemberService.class);
             OrderService orderService = applicationContext.getBean("orderService",
            											OrderService.class);
             long memberId = 1L;
             Member member = new Member(memberId, "memberA", Grade.VIP);
             memberService.join(member);
             Order order = orderService.createOrder(memberId, "itemA", 10000);
             
             System.out.println("order = " + order);
         }
    }

    OrderApp 도 마찬가지로 ApplicationContext에서 빈을 꺼내와서 생성자 주입을 해줍니다.

     

     

    스프링 컨테이너

    - 우선 ApplicationContext를 바로 스프링 컨테이너라 합니다.

     

    - 기존에는 개발자가 AppConfig를 사용해서 직접 객체를 생성하고 DI를 했지만, 이제부터는 스프링 컨테이너를 통해서 사용합니다.

     

    - 스프링 컨테이너는 @Configuration 이 붙은 AppCOnfig를 설정 정보로 사용합니다. 여기서 @Bean이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록합니다. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 합니다.

     

    - 스프링 빈은 @Bean 이 붙은 메서드 명을 스프링 빈의 이름으로 사용합니다. (memberService, orderService)

    이전에는 개발자가 필요한 객체를 AppConfig 를 사용해서 직접 조회했지만 이제부터는 스프링 컨테이너를 통해서 필요한 스프링 빈을 찾아야 합니다.

     

    - 스프링 빈은 applicationContext.getBean() 메서드를 사용해서 찾을 수 있습니다.

     

    - 기존에는 개발자가 직접 자바 코드로 모든 것을 했다면 이제부터는 스프링 컨테이너에 객체를 스프링 빈으로 등록하고, 스프링 컨테이너에서 스프링 빈을 찾아서 사용하도록 변경되었습니다.

     

    - 코드가 약간 더 복잡해진 것 같은데, 스프링 컨테이너를 사용하면 어떤 장점이 있을까요?

     

     

    스프링 컨테이너 생성

    스프링 컨테이너가 생성되는 과정을 알아봅시다.

    //스프링 컨테이너 생성
    ApplicationContext applicationContext =
                             new
                            AnnotationConfigApplicationContext(AppConfig.class);

    - ApplicationContext를 스프링 컨테이너라 합니다.

     

    - ApplicationContext는 인터페이스입니다.

     

    - 스프링 컨테이너는 XML 기반으로 만들 수 있고, 애노테이션 기반의 자바 설정 클래스로 만들 수 있습니다.

     

    - 직전에 AppConfig를 사용했던 방식이 아닌 애노테이션 기반의 자바 설정 클래스로 스프링 컨테이너를 만든 것입니다.

     

    - new AnnotationConfigApplicationContext(AppConfig.class) 이 클래스는 ApplicationContext 인터페이스의 구현체입니다.

     

    참고 : 더 정확히는 스프링 컨테이너를 부를 때 BeanFactory, ApplicationContext로 구분해서 이야기합니다. BeanFactory는 사용하는 경우가 거의 없으므로 일반적으로 ApplicationContext를 스프링 컨테이너라 합니다.

     

     

    스프링 컨테이너를 생성할 때는 구성 정보를 지정해주어야 하는데 여기서는 AppConfig.class를 구성 정보로 지정했습니다.

     

    스프링 빈 의존관계 설정 - 준비

     

    스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용해서 스프링 빈을 등록합니다.

    빈 이름은 일반적으로 메서드 이름을 사용하는데 @Bean(name = "memberService2") 이렇게 직접 부여할 수도 있습니다.

     

    주의 : 빈 이름은 항상 다른 이름을 부여해야 합니다. 같은 이름을 부여하면, 다른 빈이 무시되거나, 기존 빈을 덮어버리거나 설정에 따라 오류가 발생합니다.

     

    스프링 컨테이너는 위와 같이 일단 메서드명에 맞게 스프링 빈을 생성해서 등록합니다.

     

     

    스프링 빈 의존관계 설정 - 완료

    스프링 컨테이너는 설정 정보를 참고해서 의존관계를 주입합니다.

    단순히 자바 코드를 호출하는 것 같이 보이지만 이 차이는 추후에 설명할 싱글톤 컨테이너에서 설명하겠습니다.

    짧게 말하자면, memberRepository를 2번 호출하기 때문에 다른 참조값을 가진 MemberRepository가 만들어질 것처럼 추측되지만 스프링 컨테이너는 어떻게 해서든 디자인 패턴 중 생성 패턴인 싱글톤을 보장해줍니다.

     

    참고

    스프링은 빈을 생성하고 의존관계를 주입하는 단계가 나누어져 있습니다. 그런데 이렇게 자바 코드로 스프링 빈을 등록하면 생성자를 호출하면서 의존관계 주입도 한 번에 처리됩니다.

     

    컨테이너에 등록된 모든 빈 조회

     

    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;
    import static org.assertj.core.api.Assertions.assertThat;
    
    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);
         	}
         }
         
         @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);
                 }
         	 }
         }
    }

    모든 빈 출력하기

    - 실행하면 스프링에 등록된 모든 빈 정보를 출력할 수 있습니다.

    - ac.getBeanDefinitionNames() : 스프링 컨테이너에 등록된 모든 빈 이름을 조회합니다.

    - ac.getBean() : 빈 이름으로 빈 객체를 조회합니다.

     

    애플리케이션 빈 출력하기

    스프링이 내부에서 사용하는 빈은 제외하고, 내가 등록한 빈만 출력합니다.

    - 스프링이 내부에서 사용하는 빈은 getRole()로 구분할 수 있습니다.

     

    (ROLE_APPLICATION : 일반적으로 사용자가 정의 한 빈, ROLE_INFRASTRUCTRUE : 스프링이 내부에서 사용하는 빈)

     

    스프링 빈 조회 - 기본

    스프링 컨테이너에서 스프링 빈을 찾는 가장 기본적인 조회 방법은

    ac.getBean(빈 이름, 반환 타입)과 ac.getBean(타입)이 있습니다.

     

    조회 대상 스프링 빈이 없으면 다음과 같은 예외가 발생합니다.

    NoSuchBeanDefinitionException: No bean named 'xxxxx' available

     

    예제

    import hello.core.AppConfig;
    import hello.core.member.MemberService;
    import hello.core.member.MemberServiceImpl;
    import org.junit.jupiter.api.Assertions;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import
    org.springframework.context.annotation.AnnotationConfigApplicationContext;
    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("빈 이름으로 조회X")
         void findBeanByNameX() {
             //ac.getBean("xxxxx", MemberService.class);
             Assertions.assertThrows(NoSuchBeanDefinitionException.class, () ->
            		ac.getBean("xxxxx", MemberService.class));
         }
    }

    참고 : 구체 타입으로 조회하면 변경 시 유연성이 떨어집니다.

     

    findBeanBynameX 메서드는 람다식으로 표현한 메서드입니다. getBean을해서 반환 타입이 MemberService인 xxxxx라는 빈을 찾았을 때 앞에 있는 NoSuchBeanDefinitionException이 발생하면 테스트가 성공합니다. 즉 지정한 오류가 발생하면 테스트가 성공하는 것입니다. 여기서의 Assertions는 jupiter.api.Assertions를 import 하는 것이 아니라

    import static org.assertj.core.api.Assertions.* 를 해야 사용이 가능합니다.

     

    스프링 빈 조회 - 동일한 타입이 둘 이상

    타입으로 스프링 빈을 조회 시 같은 타입의 스프링 빈이 둘 이상이면 오류가 발생합니다. 이때는 빈 이름을 지정해줘야 합니다. ac.getBeansOfType() 메서드를 사용하면 해당 타입의 모든 빈을 조회할수 있습니다.

     

    예제

    import hello.core.member.MemberRepository;
    import hello.core.member.MemoryMemberRepository;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
    import
    org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import java.util.Map;
    import static org.assertj.core.api.Assertions.assertThat;
    import static org.junit.jupiter.api.Assertions.assertThrows;
    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);
             assertThat(beansOfType.size()).isEqualTo(2);
         }
         
         @Configuration
         static class SameBeanConfig {
             @Bean
             public MemberRepository memberRepository1() {
             	return new MemoryMemberRepository();
             }
             
             @Bean
             public MemberRepository memberRepository2() {
             	return new MemoryMemberRepository();
             }
         }
    }

    타입으로 조회시 같은 타입이 둘 이상 있으면 NoUniqueBeanDefinitionException 이 발생합니다. 그렇기 때문에 구분할 수 있도록 빈 이름을 다르게 지정해줘야 합니다. isInstanceOf는 말 그대로 하나의 인스턴스의 타입이 파라미터로 주어진 타입과 비교하는 것입니다.

    또한 특정 타입을 모두 조회하기 위해서는 ac.getBeansOfType 메서드를 사용해서 Map에다가 담아주면 key 값으로 빈의 이름이 나오고 value 값으로 참조값이 나옵니다.

     

     

    스프링 빈 조회 - 상속 관계

    스프링 빈을 조회할 때 부모 타입으로 조회하면 자식 타입도 함께 조회됩니다. 그래서 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면 모든 스프링 빈을 조회하게 됩니다.

     

     

     

    예제 코드

    import hello.core.discount.DiscountPolicy;
    import hello.core.discount.FixDiscountPolicy;
    import hello.core.discount.RateDiscountPolicy;
    import hello.core.member.MemberService;
    import hello.core.member.MemberServiceImpl;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
    import
    org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import java.util.Map;
    import static org.assertj.core.api.Assertions.assertThat;
    import static org.junit.jupiter.api.Assertions.assertThrows;
    
    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();
             }
         }
    }

    @Configuration 이 달린 TestConfig 클래스를 만들어서 테스트를 해보겠습니다.

     

    우선 findBeanByParentTypeDuplicate()에서 부모 타입으로 조회를 할 때 자식이 둘 이상 있으면 중복 오류가 발생합니다.(NoUniqueBeanDefinitionException)

    이럴 경우에는 빈 이름을 직접 지정해서 빈을 조회하면 오류가 발생하지 않습니다.

     

    또한, 인페이스를 구현하는 구현체의 특정 타입으로(하위 타입) 조회할 수 있는데 이것은 아무래도 좋지 않은 방법입니다.

     

    부모 타입으로 모두 조회하기 위해서는 findAllBeanByParentType() 메서드의 로직처럼 ac.getBeansOfType를 통해서 부모 타입을 모두 조회해서 Map에 담아 준 다음에 조회해주면 됩니다.

     

    Object로 조회를 하게 되면 다음과 같이 직접 생성한 빈을 보여줄 뿐만 아니라 스프링이 자체적으로 빈으로 등록해서 사용하는 모든 빈들을 조회할 수 있습니다.

     

    BeanFactory와 ApplicationContext

    이번에는 beanFactory와 ApplicationContext에 대해서 알아보겠습니다.

     

    Bean Factory

    - 스프링 컨테이너의 최상위 인터페이스입니다.

    - 스프링 빈을 관리하고 조회하는 역할을 담당합니다.

    - getBean()을 제공합니다.

    - 지금까지 우리가 사용했던 대부분의 기능은 BeanFactory가 제공하는 기능입니다.

     

    ApplicationContext

    - BeanFactory 기능을 모두 상속받아서 제공합니다.

    - 빈을 관리하고 검색하는 기능을 BeanFactory가 제공해주는데, 그러면 둘의 차이가 뭘까?

    - 애플리케이션을 개발할 때는 빈은 관리하고 조회하는 기능은 물론이고, 수많은 부가기능이 필요합니다.

     

    ApplicationContext가 제공하는 부가기능

    메시지 소스를 활용한 국제화 기능

     - 예를 들어서 한국에서 들어오면 한국어로, 영어권에서 들어오면 영어로 출력

     

    환경변수

    로컬, 개발, 운영 등을 구분해서 처리

     

    애플리케이션 이벤트

    - 이벤트를 발행하고 구독하는 모델을 편리하게 지원

     

    편리한 리소스 조회

    파일, 클래스 패스, 외부 등에서 리소스를 편리하게 조회

     

    이 부분은 저도 공부를 아직 하지 않은 부분이라서, 실무에서는 다양한 기능이 필요로 하기 때문에 BeanFactory를 사용하는 것이 아니라 더 많은 기능을 제공하는 하위 인터페이스인 ApplicationContext를 사용한다고 이해하고 넘어가겠습니다.

     

    다음 포스팅에서는 스프링 컨테이너를 설정할 때 XML 파일을 사용해서 하는 법을 알아보겠습니다!

Designed by Tistory.