Spring

[Spring] Spring 핵심 원리 : 순수한 Java 애플리케이션 - Spring 적용

yjk490 2023. 6. 25. 14:57

스프링 컨테이너와 스프링 빈

스프링 컨테이너는 의존성 주입을 해주는 DI 컨테이너이다. 앞서 설명했던 AppConfig와 같은 역할을 한다. 그런데 AppConfig를 사용할 때는 개발자가 객체를 직접 생성했지만 스프링 프레임워크를 사용하면 스프링 컨테이너로부터 객체를 가져온다.

스프링 컨테이너는 개발자의 설정 정보를 바탕으로 애플리케이션 실행에 필요한 객체들을 생성하고 스프링 컨테이너에 등록한다. 그리고 객체들 간의 의존관계를 자동으로 맺어준다. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라 부른다.

스프링 컨테이너 생성

ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

ApplicationContext가 스프링 컨테이너이다. ApplicationContext는 인터페이스이기 때문에 스프링 컨테이너를 생성하는 방법에 따라 여러 구현체가 있다. 그중에서 애노테이션 기반의 Java 설정 클래스를 활용하는 구현체가 AnnotationConfigApplicationContext이다. 생성자의 매개변수로 AppConfig.class를 전달하는데, 이는 앞서 언급한 스프링 컨테이너의 설정 정보로 AppConfig를 사용하는 것이다.

@Configuration
public class AppConfig {

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

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

    @Bean
    public OrderRepository orderRepository() {
        return new MemoryOrderRepository();
    }

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

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

스프링 컨테이너는 @Configuration이 적힌 클래스를 설정 정보로 사용한다. 그리고 @Bean이 적힌 메서드를 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. 이렇게 등록된 객체가 앞서 설명했듯이 스프링 빈이다.  스프링 빈은 빈 이름이 객체의 주소값과 함께 쌍의 형태로 저장된다. 빈 이름의 기본값은 메서드 이름이고 직접 부여할 수도 있다. 다만 반드시 기본값과 다른 이름으로 부여해야 한다.

지금의 코드는 스프링 빈이 등록되면서 바로 의존성 주입이 되는 형태이지만 실제로는 빈 등록과 의존성 주입 단계가 나누어져 있다. 자세한 설명은 이후 자동 의존성 주입(Autowired)와 함께 설명한다.

실행

public class OrderApp {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        OrderService orderService = ac.getBean("orderService", OrderService.class);

        Member savedMember = memberService.signUp(new Member(1L, "memberA", Grade.VIP));
        Order savedOrder = orderService.createOrder(savedMember.getMemberId(), "3series", 50000);
        Order foundOrder = orderService.findOrder(savedOrder.getOrderId());

        System.out.println("savedOrder = " + savedOrder.toString());
        System.out.println("foundOrder = " + foundOrder.toString());
    }
}
/* 실행 결과
14:53:55.703 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@43738a82
14:53:55.726 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
14:53:55.955 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
14:53:55.959 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
14:53:55.960 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
14:53:55.963 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
14:53:55.974 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
14:53:55.981 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberService'
14:53:56.002 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberRepository'
14:53:56.005 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'
14:53:56.007 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderRepository'
14:53:56.009 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'discountPolicy'
savedOrder = Order{orderId=1, memberId=1, itemName='3series', itemPrice=50000, discountPrice=1000}
foundOrder = Order{orderId=1, memberId=1, itemName='3series', itemPrice=50000, discountPrice=1000}

Process finished with exit code 0
*/

AppConfig를 직접 실행했을 때와는 다른 로그다. 자세히 알수는 없지만 로그 아래쪽을 보면 @Bean을 통해 스프링 빈으로 등록했던 빈 이름을 확인할 수 있다.