Spring Container, Bean

이용만·2023년 4월 3일
0
public class MemberService {

	//memberservice는 memberrepository 객체에 의존하고 있다.
    private final MemberRepository memberRepository = new MemberRepository();
    
    //memberRepository 메서드 사용, 강한결합
    public void createMember(Member member){
        memberRepository.postMember(member);
    }

    public Member getMember(Long memberId){
        return memberRepository.getMember(memberId);
    }

    public void deleteMember(Long memberId){
        memberRepository.deleteMember(memberId);
    }
}
    

MemberRepository라는 객체를 다른 MockRepository 라는 객체로 교체하면 아래 구현한 메서드들이 새롭게 의존관계를 설정한 MockRepository라는 객체에 존재하지 않으면 해당 코드도 모두 변경해야하고 객체간의 관계가 변경될 때마다 우리가 직접 해당 코드를 찾아 수정하고 문제점이 없는지 살펴봐야 한다.

package com.codestates.section2week4.member;

public class MemberService {

////    memberservice는 memberrepository 객체에 의존하고 있다.
//    private final MemberRepository memberRepository = new MemberRepository();

    private MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    public void createMember(Member member){
        memberRepository.postMember(member);
    }

    public Member getMember(Long memberId){
        return memberRepository.getMember(memberId);
    }

    public void deleteMember(Long memberId){
        memberRepository.deleteMember(memberId);
    }
}

생성자를 통해 의존성을 주입받음으로써 우리는 객체가 생성되는 순간 의존관계를 설정을 한다.

여기서의 문제는 객체를 생성할때마다 결국 직접 주입할 객체를 직접 작성해서 생성자로 넣어줘야한다.

public class DependencyConfig {
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }

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

    public CoffeeService coffeeService(){
        return new CoffeeService(coffeeRepository());
    }

    public CoffeeRepository coffeeRepository(){
        return new CoffeeRepository();
    }
}

지금까지는 프레임워크의 도움이 아닌, 직접 설정 파일을 통해 의존성 주입을 할 수 있는 방식을 사용했다.
이제 스프링에서 지원하는 DI를 학습해보자.

🔎Spring Container

스프링 컨테이너는 내부에 존재하는 애플리케이션 빈(Bean)의 생명주기를 관리한다.
개발자가 정의한 Bean을 객체로 만들어 Bean의 생성, 관리, 제거등의 역할을 하고
개발자가 필요로 할 때 제공한다.
🙂 : "아, 스프링 컨테이너는 빈의 생명주기를 관리하는 녀석이구나."

🧐왜 사용하지?
객체지향 프로그래밍은 낮은 결합과 캡슐화가 특징이다. 그러나 서로 다른 클래스가 참조가 심할 수록 의존성이 높아지고 이는 객체지향적이지 못하다. 객체간 의존성을 낮추고 높은 캡슐화를 위해 Spring 컨테이너가 사용된다.
구현 클래스에 의존하는게 아니라 인터페이스에 의존하도록 설계한다.(PSA)

✒️어떻게 스프링 컨테이너가 생성되는가?

ApplicationContext 인터페이스 구현체를 통해 스프링 컨테이너를 만든다.

  • DependencyConfig.class 등의 구성 정보를 지정해줘서 스프링 컨테이너를 생성한다.
  • DependencyConfig에 있는 구성 정보를 통해서 스프링 컨테이너는 필요한 객체들을 생성한다.
  • 애플리케이션 클래스는 구성 메타데이터와 결합되어 ApplicationContext 생성 및 초기화된 후 완전히 구성되고 실행 가능한 시스템 또는 애플리케이션을 갖는다.
// Spring Container 생성
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(DependencyConfig.class);
  1. 스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용해서 스프링 빈을 등록합니다.
  2. new AnnotationConfigApplicationContext(구성정보.class)로 스프링에 있는 @Bean의 메서드를 등록합니다.

📌스프링 컨테이너의 종류

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

  • BeanFactory는 빈을 등록하고 생성하고 조회하고 돌려주는 등 빈을 관리하는 역할을 합니다.
  • getBean() 메소드를 통해 빈을 인스턴스화할 수 있습니다.
  • @Bean이 붙은 메서드의 명을 스프링 빈의 이름으로 사용해 빈 등록을 합니다.

ApplicationContext : BeanFactory의 기능을 상속받아 제공합니다.

  • 빈을 관리하고 검색하는 기능을 BeanFactory가 제공하고 그 외에 부가기능을 제공합니다.

🔎Bean

  • 스프링 컨테이너에 등록된 객체를 스프링 빈이라 한다.
  • 스프링 빈은 @Bean 이 붙은 메서드의 명을 스프링 빈의 이름으로 사용한다. (memberService)
  • 이전에는 개발자가 필요한 객체를 AppConfig 를 사용해서 직접 조회했지만, 이제부터는 스프링 컨테이너 를 통해서 필요한 스프링 빈(객체)를 찾아야 한다. 스프링 빈은 applicationContext.getBean() 메서드 를 사용해서 찾을 수 있다.
  • 이전에는 개발자가 필요한 객체를 AppConfig 를 사용해서 직접 조회했지만, 이제부터는 스프링 컨테이너 를 통해서 필요한 스프링 빈(객체)를 찾아야 한다. 스프링 빈은 applicationContext.getBean() 메서드 를 사용해서 찾을 수 있다.
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

PetStoreService service = context.getBean("memberRepository", memberRepository.class);

getBean 을 사용하여 bean의 인스턴스를 가져올 수 있으나 응용 프로그램 코드에서는 getBean() 메서드로 호출하여 사용하면 안된다.



스프링은 다양한 환경 설정을 지원한다. 스프링은 어떻게 이런 다양한 설정 형식을 지원할까?

BeanDefinition
참고 : https://velog.io/@jy9922/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88-%EC%84%A4%EC%A0%95-%EB%A9%94%ED%83%80-%EC%A0%95%EB%B3%B4-BeanDefinition

  • 추상화 개념, 빈 설정 메타 정보
    • @Bean 당 각각 하나씩 메타 정보가 생성된다.
    • 스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈을 생성한다.
  • 역할과 구현을 개념적으로 나눈 것
    • XML을 읽어서 BeanDefinition을 만들면 된다.
    • 스프링 컨테이너는 자바 코드인지, XML 코드인지 몰라도 된다.
    • 오직 BeanDefinition만 알면 된다.
    • 즉, 스프링 컨테이너는 추상화만 알면 된다.

  • AnnotationConfigApplicationContext는 AnnotatedBeanDefinitionReader를 사용해서 AppConfig.class를 읽고 BeanDefinition을 생성한다.
  • GenericXmlApplicationContext는 XmlBeanDefinitionReader를 사용해서 appConfig.xml를 읽고 BeanDefinition을 생성한다.
  • 새로운 형식의 설정 정보가 추가되면, XxxBeanDefinitionReader를 만들어서 BeanDefinition을 생성하면 된다.

'아.. BeanDefinition이란 추상화 때문에 스프링에서 다양한 설정 형식을 지원하는구나!'


🔎Spring Container 생성과정

new AnnotationConfigApplicationContext(AppConfig.class)

스프링 컨테이너를 생성할 때는 구성 정보를 지정해주어야 한다. 여기서는 AppConfig.class 를 구성 정보로 지정했다.

  • 스프링 빈 등록
    • 스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용해서 스프링 빈을 등록한다.
    • 빈이름은 항상 중복되지 않아야 한다.
    • @Bean은 메소드 이름을 빈이름으로 사용한다.

  • 스프링 의존관계 주입
    • 스프링 컨테이너는 설정 정보를 참고해서 의존관계를 주입(DI)한다.
    • 단순히 자바 코드를 호출하는 것 같지만, 차이가 있다. 이 차이는 뒤에 싱글톤 컨테이너에서 설명한다.

핵심 포인트
컨테이너의 의미
소프트웨어 개발 용어의 관점에서 컨테이너란 내부에 또 다른 컴포넌트를 가지고 있는 어떤 컴포넌트를 의미한다.

  • 컨테이너는 먼저 객체를 생성하고 객체를 서로 연결합니다.
  • 객체를 설정하는 단계를 지나 마지막으로 생명주기 전반을 관리합니다.
  • 컨테이너는 객체의 의존성을 확인해 생성한 뒤 적절한 객체에 의존성을 주입합니다.
  • 스프링은 스프링 컨테이너를 통해 객체를 관리합니다.
  • 스프링 컨테이너에서 관리되는 객체를 빈(Bean)이라고 합니다.
profile
성장하는 개발자가 되고자 합니다.

0개의 댓글