[Spring] 스프링 컨테이너 (애노테이션 기반, XML 기반)

728x90

 

스프링 컨테이너 (애노테이션 기반 자바 코드 설정)

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

 

ApplicationContext 스프링 컨테이너라고 한다.

이때, ApplicationContext 는 인터페이스이며, AnnotationConfigApplicationContext 는 인터페이스의 구현체이다.

스프링 컨테이너는 생성자가 클래스 정보를 받으면, 해당 클래스가 스프링 빈으로 자동 등록된다.

 

스프링 컨테이너는 두 가지 방식으로 설정할 수 있다.

1. XML 기반 (거의 사용 X)

2. 애노테이션 기반의 자바 설정 클래스 (거의 대부분)

 

스프링 컨테이너 생성 과정

1. @Configuration 이 붙은 AppConfig를 설정 정보로 사용하여

new AnnotationConfigApplicationContext(AppConfig.class) 를 통해 스프링 컨테이너를 생성한다.

2. @Bean 으로 등록된 메서드를 모두 호출하여 스프링 빈으로 등록한다.

2. 등록된 빈들을 스프링 빈 저장소에 스프링 빈 이름, 빈 객체 짝지어 저장하여 싱글톤으로 관리한다.

이때, 스프링 빈 이름은 @Bean 이 붙은 메서드 명으로 사용한다.

( @Bean(name="aa") 로 이름을 직접 부여할 수도 있다.)

3. 스프링 컨테이너가 생성되면 스프링 빈들 간의 의존관계를 자동으로 주입(DI)한다.

 

>참고<

- 빈 이름은 항상 달라야 한다. (다른 빈 무시 or 기존 빈 덮어버림 등 오류 발생)

 

public class MemberApp {
  public static void main(String[] args) {
  
    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());
    }
  }
}

 

싱글톤 컨테이너

스프링 컨테이너로 스프링 빈을 등록하면, 싱글톤으로 관리한다.

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

 

@Configuration이 붙은 AppConfig 로 스프링 컨테이너를 생성할 때,

실제 클래스가 아닌 프록시 객체를 생성하여 스프링 빈으로 등록한다.

 

따라서 임의의 다른 클래스를 통해

@Bean 이 붙은 메서드마다 이미 스프링 컨테이너에 등록되어 있으면 존재하는 빈을 반환하고, 

스프링 컨테이너에 등록되어 있지 않으면 생성하여 스프링 빈으로 등록하고 반환하는 코드가 실행된다.

 

// AppConfig@CGLIB 예상 코드
@Bean
public MemberRepository memberRepository() {

  if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
    return 스프링 컨테이너에서 찾아서 반환;
  } else { //스프링 컨테이너에 없으면
    기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
    return 반환
  }
}

 

이러한 프록시 객체 덕분에 싱글톤이 보장된다.

 

>참고<

- @Configuration 없이 @Bean만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지 않는다

 

스프링 컨테이너(XML 설정)

컴파일 없이 빈 설정 정보를 변경할 수 있다.

GenericXmlApplicationContext 를 사용하여 XML 설정 파일을 넘기면 된다.

 

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

// 아래와 동일한 설정이다.
@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();
    }
}

 

위와 같은 xml 설정 파일을 resources 디렉토리에 생성하여 사용해 준다.

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

 

BeanFactory 와 ApplicationContext

 

BeanFactory 나 ApplicationContext 를 스프링 컨테이너라고 한다.

보통 부가기능이 포함된 ApplicationContext 를 사용한다.

 

BeanFactory

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

스프링 빈을 관리, 조회하는 역할을 담당한다.

getBean( ) 및 대부분의 기능을 제공한다.

 

ApplicationContext

BeanFactory 기능을 모두 상속받아 제공한다.

빈을 관리, 조회하는 역할 외의 아래와 같은 부가기능을 제공한다.

메시지 소스를 활용한 국제화 기능 : 한국에서 들어오면 한국어로, 영어권에서 들어오면 영어로 출력

환경변수 : 로컬, 개발, 운영 등 구분해서 처리

애플리케이션 이벤트 : 이벤트를 발행, 구독하는 모델을 편리하게 지원

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

 

 

 

 

 

 

 

출처 | 스프링 핵심 원리 - 기본편(김영한) - 인프런

728x90