개발/Spring Core

Spring 핵심원리 객체 지향 원리 적용 (2), 빈 조회, BeanDefinition

Debin 2021. 12. 30.
반응형
본 게시글은 인프런 김영한 선생님 강의 스프링 핵심 원리를 완강하고 배운 것을 남기고자 적은 포스팅입니다.

강의 링크는 아래와 같습니다.

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

 

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

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

www.inflearn.com

 

자 이제 순수 자바 코드에서 스프링으로 넘어와보자. 우리는 AppConfig라는 설정 클래스를 지난 시간에 만들었다.

이제 이 AppConfig를 스프링으로 전환해보겠다.

 

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

//못보던 코드가 있지만 양해부탁드립니다.

 

AppConfig에 설정을 구사한다는 애노테이션 @Configuration을 붙여준다.

각 메서드에 @Bean을 붙인다. 이렇게 하면 스프링 컨테이너에 스프링 빈으로 등록된다. 메서드 이름으로 스프링 빈에 등록된다.

 

그리고 이제 MemberApp이라는 클래스를 만들어 스프링을 적용한 애플리케이션을 살펴보겠다.

 

import spring.core.member.Grade;
import spring.core.member.Member;
import spring.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());
   }
}

 

스프링 컨테이너

  • ApplicationContext는 스프링 컨테이너라고 한다.
  • 기존에는 개발자가 AppConfig를 사용해서 직접 객체를 생성하고 Di 했지만, 이제부터는 스프링 컨테이너를 이용해 등록한다.
  • 스프링 컨테이너는 @Configuration이 붙은 AppConfig를 설정 정보로 사용한다. @Bean이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다. 스프링 빈은 보통 메서드의 명을 스프링 빈 이름으로 사용한다.
  • 이전에는 개발자가 필요한 객체를 AppConfig를 사용해서 직접 조회했지만, 이제부터는 스프링 컨테이너를 통해 스프링 빈을 찾아야 한다. 이때 applicationContext.getBean() 메서드를 이용해 빈을 찾을 수 있다.

 

ApplicationContext는 스프링 컨테이너이자, 인터페이스다. 스프링 컨테이너는 XML 기반으로도 만들 수 있고 애노테이션 기반의 자바 설정 클래스로도 만들 수 있다. AppConfig는 애노테이션 기반의 자바 설정 클래스로 스프링 컨테이너를 만든 것이다. AnnotationConfigApplication 은 자바 설정 클래스를 기반으로 스프링 컨테이너를 만들며, 또한 ApplicationContext 인터페이스의 구현체다.

 

1. 스프링 컨테이너 생성 (지금의 경우는 싱글톤 방식이다)

 

 

2. 스프링 빈 등록

 

 

빈 이름은 메서드로 저장되지만 우리가 별도로 설정할 수 있다.

 

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

 

 

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

 

 

뒤에서 한번 더 적겠지만 new 연산자라 해도 같은 타입은 1개씩 생성된다. (싱글톤 컨테이너)

스프링은 빈을 생성하고, 의존 관계를 주입하는 2단계로 나뉜다. 그런데 이렇게 자바 코드로 스프링 빈을 등록하면 생성자를 호출하면서 의존관계 주입도 한 번에 처리된다.

 

이제는 스프링의 빈들을 출력해보자.

AnnotationConfigApplicationContext의 메서드인 getBeanDefinitionNames를 이용하면 빈들의 이름이 문자열 배열로 리턴된다. 이를 for 문을 이용하면 모든 빈들을 확인할 수 있다.

우리가 만든 애플리케이션 빈들만 확인하고 싶다면 코드는 아래와 같다.

//모든 빈들을 문자열 배열로 얻었다는 가정 하에 시작.

for(String beanName: beanNames){
	BeanDefinition beanDefinition=ac.getBeanDefinition(beanName);
    if(beanDefinition.getRole()==BeanDefinition.ROLE_APPLICATION){
    	Object bean= ac.getBean(beanName);
        System.out.println(bean); 
    }
}

위와 같은 코드를 통해 우리가 만든 애플리케이션 빈들만 확인이 가능하다.

 

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

ac.getBean(빈 이름, 타입);

ac.getBean(타입); 

 

동일한 타입이 2개 이상일 때, 타입으로 조회하면 같은 타입의 스프링 빈이 2개 이상이어서 오류가 발생한다. 이때는 서로 빈 이름을 지정해 스프링 빈을 조회한다. ac.getBeansOfType()을 사용하면 해당 타입의 모든 빈이 조회가 가능하다.

 

스프링 빈 조회에서 상속 관계는 중요하다.

바로 부모 타입으로 조회하면, 모든 자식 타입도 함께 조회되기 때문이다.

그래서 자바 객체 최고 부모인 Object로 조회를 하면, 모든 스프링 빈을 조회한다.

이때도 마찬가지로 부모 타입으로 조회 시, 자식이 둘 이상 있으면 오류가 발생한다. 그러면 빈 이름을 지정해줘야 한다.

 

 

이제 BeanFactory와 ApplicationContext에 대해 알아보겠다.

 

먼저 BeanFactor는 스프링 컨테이너의 최상위 인터페이스다. 스프링 빈을 관리하고 조회하는 역할을 담당한다. getBean 메서드를 제공하며 우리는 대부분 이 BeanFactory의 기능을 사용했다.

 

Application Context는 BeanFactory의 기능을 모두 상속받아 제공하며, 더 많은 부가적인 기능을 제공한다.

 

 

이 ApplicationContext 인터페이스를 상속받는 게 AnnotationConfigApplicationContext다. 이 세 가지는 모두 스프링 컨테이너다.

 

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

 

 

위 사진과 같이 자바 코드, xml, 그 밖의 형식으로 설정 정보를 받아들일 수 있다. xml로 설정 실습도 해보았지만 중요하다고 생각되지 않아서 pass

 

스프링이 이렇게 다양한 설정 형식을 지원하는 중심에는 BeanDefinition이라는 추상화가 있다.

쉽게 이야기해서 역할과 구현을 개념적으로 나눈 것이다.

XML을 읽어서 BeanDefinition을 만들고, 자바 코드를 읽어서 BeanDefinition을 만든다. 스프링 컨테이너는 어떤 형식의 설정 정보가 작성되어 있는지 몰라도 된다. 오직 BeanDefinition만 알면 된다. BeanDefinition을 빈 설정 메타 정보라고 한다.

 

BeanDefinition에 대해서는 너무 깊이 있게 이해하기보다는, 스프링이 다양한 형태의 설정 정보를 BeanDefinition으로 추상화해서 사용하는 것 정도만 이해하면 된다. 

 

이상 마치겠습니다.

반응형

댓글