article thumbnail image
Published 2022. 1. 10. 21:56
반응형

ApplicationContext란


서론

스프링은 객체지향의 장점을 살리기 위해 EJB시절 로드 존슨이 취미로 만들기 시작하나 3만 줄의 예제 코드부터 시작 되었다.
객체 지향의 장점이란 변화에 대응하기 쉬운 코드를 말한다. 하지만 EJB는 객체지향과는 거리가 멀고 너무 변경에 어려웠다고 하며,
이때 나온 말이 POJO라고 한다. 순수 자바로 돌아가서 객체지향적 개발을 하자는 취지의 용어이다.

본론

  • ApplicationContext는 스프링의 DI 컨테이너이다. 변경에 용이하기 위해 DI 컨테이너에서 의존 객체를 주입 해주기 위해
    여러 객체들을 관리하는 상자라고 생각하면 된다.
  • 인터페이스이다.

순수 자바로 DI 컨테이너와 스프링의 DI 컨테이너


아래는 순수 자바로 DI 컨테이너를 만든 코드이다.
간단한 코드이므로 설명을 생략한다.

public class AppConfig {

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

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

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

    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }
}

// 아래는 AppConfig를 직접 관리하고 사용하는 코드이다.
public class MemberApp {

    public static void main(String[] args) {
        AppConfig appConfig = new AppConfig();
        MemberService memberService = appConfig.memberService();

        Member member = new Member(1L, "memberA", Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findMember(1L);
        System.out.println("member = " + member.getName());
        System.out.println("findMember = " + findMember.getName());
    }
}

아래는 스프링의 DI 컨테이너를 사용하는 코드이다.

@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) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = ac.getBean("memberService", MemberService.class);

        Member member = new Member(1L, "memberA", Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findMember(1L);
        System.out.println("member = " + member.getName());
        System.out.println("findMember = " + findMember.getName());
    }
}

차이를 보자면 @Configuration, @Bean이라는 키워드가 있다는 것이다.
스프링에서는 두 키워드가 바로 스프링의 DI 컨테이너에 등록할 수 있도록 해주는 키워드이다.

@Configuration이 있는 클래스에 @Bean이라는 키워드가 붙은 메서드를 모두 DI 컨테이너에 등록하게 된다.
즉, 개발자가 직접 객체를 관리하는 것이 아닌 스프링 컨테이너가 관리하는 것의 차이이다.

  • 엄밀히 말하면 @Bean이 달린 것을 컨테이너에 등록하는 것이고 @Configuration은 싱글톤으로 생성할지 말지를 결정하는 요소가 된다.
    즉, 반드시 @Configuration이 있어야만하는 것은 아니다.

AnnotationConfigApplicationContext


  • ApplicationContext의 구현체이다.
  • new AnnotationConfigApplicationContext(AppConfig.class)의 코드로 위에서 사용되고 있는데
    AppConfig클래스에 있는 @Bean이 달린 메서드를 스프링 컨테이너에 등록하는 코드이다. 이 코드를 작성하면
    모두 등록이 되어 ac.getBean("memberService", MemberService.class)을 사용하여 사용할 수 있게 된다.
  • 이름에서 보이듯 어노테이션 기반의 설정 정보를 읽어 스프링 컨테이너에 등록하게 된다.

스프링 컨테이너에 등록했을 때 로그


스프링 컨테이너에 등록 했을 때와 등록하지 않았을 때의 차이가 있다. 등록하지 않았을 때는 아무런 로그도 없지만,
등록하게 되면 스프링 컨테이너에 싱글톤 빈으로 등록 되었다는 로그를 볼 수 있다. 이 빈들은 AppConfig클래스의
@Bean이 붙었던 메서드들의 빈 이름을 보여준다.

스프링의 빈 의존관계 설정 단계


의존관계에 필요한 객체들을 생성해서 등록하는 단계와 생성된 객체들을 참조시키는 의존관계 설정 단계로 크게 2단계로 구분할 수 있다.

빈을 등록하는 단계

  • AppConfig에 있던 @Bean이 달린 메서드들을 컨테이너에 메서드명으로 빈의 이름을 생성하고, 인스턴스는 구현체를 등록하게 된다.
  • 아직 의존관계는 설정되지 않은 상태이다.

의존관계 설정 단계

  • 컨테이너에 등록된 빈들을 의존 관계를 맺는 단계이다.
    예로 아래 코드를 보자. memberService라는 빈은 memberRepository에 의존하고 있다. 이렇게 의존관계를 설정하는 단계이다.
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

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

하지만 반전은 이렇게 자바 설정으로 스프링 빈을 등록을 하게 되면 실제로는 2단계가 한번에 이루어지게 된다.
즉, 현재 이 코드들은 2단계가 한 번에 이루어지게 되는 코드이지만, 스프링 자체에서는 단계가 나누어져 있다고 한다.
이 부분은 나중에 더 살펴보도록 하겠다.

해당 코드들에서 2단계가 통합되는 것은 당연한데 왜냐하면, memberService를 빈으로 등록하려면 당연히 memberRepository도 필요하기 때문에
빈 등록과 의존관계 설정이 같이 이루어질 수 밖에 없다.

반응형
복사했습니다!