티스토리 뷰

  본 포스팅은 인프런 김영한 강사님의 스프링 부트 핵심 원리 강의 섹션 4를 수강하고 배운 점을 정리했습니다.

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 백엔드 개발자가 되어보세요! 📢

www.inflearn.com


스프링 컨테이너 생성 과정

1. 스프링 컨테이너 생성

- 스프링 컨테이너를 생성할 때는 구성 정보를 지정해주어야 한다. 아래와 같이 AppConfig.class를 구성 정보로 지정했다. 

new AnnotationConfigApplicationContext(AppConfig.class)

 

 

2. 스프링 빈 등록

AppConfig.class 

@Configuration
public class AppConfig {

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

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

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

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

 

스프링 컨테이너는 AppConfig 정보를 사용해서 스프링 컨테이너의 스프링 빈 저장소에 객체를 등록한다. 

 

스프링 컨테이너의 스프링 빈 저장소

 빈 이름 빈 객체
memberService MemberServiceImpl@x01
orderService OrderServiceImpl@x02
memberRepository MemoryMemberRepository@x03
discountPolicy RateDiscountPolicy@x04

- 스프링 컨테이너는 설정 정보를 참고해서 의존 관계를 주입(DI)한다.

⚠️ 빈 이름은 항상 다른 이름을 부여해야 한다.

 

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

1. 스프링 컨테이너에 등록된 '모든' 빈 정보 출력 테스트

 

package hello.core.beanfind;

import hello.core.AppConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public 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);
        }
    }
    
}

- 실행하면 스프링에 등록된 모든 빈 정보를 출력할 수 있다.
- ac.getBeanDefinitionNames() : 스프링에 등록된 모든 빈 이름을 조회한다.
- ac.getBean() : 빈 이름으로 빈 객체(인스턴스)를 조회한다.

팁:  위와 같은 for문 작성 시 'iter' 단축키 선택하면 자동으로 for문이 생성된다.

 

 

결과 - 스프링 내부에서 등록한 빈도 함께 출력됨. 

 

name = org.springframework.context.annotation.internalConfigurationAnnotationProcessor object= org.springframework.context.annotation.ConfigurationClassPostProcessor@6475472c
name = org.springframework.context.annotation.internalAutowiredAnnotationProcessor object= org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@3f07b12c
name = org.springframework.context.annotation.internalCommonAnnotationProcessor object= org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@4bd1f8dd
name = org.springframework.context.event.internalEventListenerProcessor object= org.springframework.context.event.EventListenerMethodProcessor@7096b474
name = org.springframework.context.event.internalEventListenerFactory object= org.springframework.context.event.DefaultEventListenerFactory@3e14c16d
name = appConfig object= hello.core.AppConfig$$EnhancerBySpringCGLIB$$f65d639a@3c989952
name = memberRepository object= hello.core.member.MemoryMemberRepository@784b990c
name = memberService object= hello.core.member.MemberServiceImpl@3d3ba765
name = orderService object= hello.core.Order.OrderServiceImpl@25bc0606
name = discountPolicy object= hello.core.discount.RateDiscountPolicy@5d1659ea

Process finished with exit code 0

 

 

2. 내가 만든 것만 보고 싶다면 '애플리케이션 빈' 출력하기

 

    @Test
    @DisplayName("애플리케이션 빈 출력하기")
    void findApplicationBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();

        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

            if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
                Object bean = ac.getBean(beanDefinitionName);
                System.out.println("name = " + beanDefinitionName + " object= " + bean);
            }
        }
    }

스프링이 내부에서 사용하는 빈은 getRole()로 구분할 수 있다.
ROLE_APPLICATION : 일반적으로 사용자가 정의한 빈
ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈

 

BeanDefinition.ROLE_APPLICATION

 

 

 

출력 결과

name = appConfig object= hello.core.AppConfig$$EnhancerBySpringCGLIB$$f65d639a@6475472c
name = memberRepository object= hello.core.member.MemoryMemberRepository@3f07b12c
name = memberService object= hello.core.member.MemberServiceImpl@4bd1f8dd
name = orderService object= hello.core.Order.OrderServiceImpl@7096b474
name = discountPolicy object= hello.core.discount.RateDiscountPolicy@3e14c16d

 

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

1. 같은 타입 두 개 이상 사용

 

public class ApplicationContextSameBeanFindTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
    
    @Test
    @DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다")
    void findByTypeDuplicate(){
        MemberRepository bean = ac.getBean(MemberRepository.class);
    }
    
    @Configuration
    static class SameBeanConfig{
        
        @Bean
        public MemberRepository memberRepository1(){
            return new MemoryMemberRepository();
        }
        
        @Bean
        public MemberRepository memberRepository2(){
            return new MemoryMemberRepository();
        }
    }
}

기존에 Config.class 파일 대신 밑에서 작성해 준 새로운 Config 클래스를 정보로 넘겨준다.

-> 빈으로 등록되는 것은 같은 타입의 빈 객체 두 개.

여기서 findByType실행 시 오류가 발생한다.

 

오류 메시지 출력

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.core.member.MemberRepository' available: expected single matching bean but found 2: memberRepository1,memberRepository2

 

 

같은 타입 key, value로 분리해서 출력

 @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);
        org.assertj.core.api.Assertions.assertThat(beansOfType.size()).isEqualTo(2);
    }

 

결과

key = memberRepository1value = hello.core.member.MemoryMemberRepository@5a85c92
key = memberRepository2value = hello.core.member.MemoryMemberRepository@32811494
beansOfType = {memberRepository1=hello.core.member.MemoryMemberRepository@5a85c92, memberRepository2=hello.core.member.MemoryMemberRepository@32811494}

 

 

스프링 빈 조회 - 상속 관계

부모 타입으로 조회하면, 자식 타입도 함께 조회한다.

가장 밑 단에 있는 4, 5, 6, 7은 자식 클래스가 없으므로 호출했을 때 자기 자신만 호출된다.

그런데 1번은 2, 3 뿐만 아니라 4, 5, 6, 7도 자식으로 갖는 최고 부모이므로 호출 시 나머지 클래스가 모두 호출된다.

같은 원리로 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면, 모든 스프링 빈을 조회한다.

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

위 코드를 실행하면 모든 스프링 빈의 정보가 콘솔에 출력된다.

 

BeanFactory와 ApplicationContext

BeanFactory

  • 스프링 컨테이너의 최상위 인터페이스다.
  • 스프링 빈을 관리하고 조회하는 역할을 담당한다.
  • getBean()을 제공한다.
  • 지금까지 우리가 사용했던 대부분의 기능은 BeanFactory가 제공하는 기능이다.

ApplicationContext

  • BeanFactory 기능을 모두 상속받아서 제공한다.
  • 빈을 관리하고 검색하는 기능을 BeanFactory가 제공해 주는데, 그러면 둘의 차이가 뭘까?
  • 애플리케이션을 개발할 때는 빈을 관리하고 조회하는 기능은 물론이고, 수많은 부가기능이 필요하다. 

 

ApplicatonContext가 제공하는 부가기능

  • 메시지소스를 활용한 국제화 기능: 예) 한국에서 들어오면 한국어로, 영어권에서 들어오면 영어로 출력
  • 환경변수: 로컬, 개발, 운영 등을 구분해서 처리
  • 애플리케이션 이벤트: 이벤트를 발행하고 구독하는 모델을 편리하게 지원
  • 편리한 리소스 조회: 파일, 클래스패스, 외부 등에서 리소스를 편리하게 조회

다양한 설정 형식 지원 - 자바 코드, XML

스프링 컨테이너는 다양한 형식의 설정 정보를 받아들일 수 있게 유연하게 설계되어 있다

Xml 기반의 스프링 빈 설정 정보: AppConfig와 같은 설정 정보

 

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

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함