[Spring 입문하기] 섹션 4. 스프링 빈과 의존관계

안나·2024년 1월 18일
0

Spring

목록 보기
4/16
post-thumbnail
이 글은 인프런에서 김영한 님의 "스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술"을 수강 후 개인적으로 공부한 내용을 정리한 게시글입니다. 잘못된 점이나 부족한 부분이 있다면 언제든 지적 부탁드립니다.

✏️ 스프링 빈(Bean)

빈(Bean)이란 스프링 컨테이너에 의해 재사용 가능한 소프트웨어 컴포넌트이다.
즉 스프링 컨테이너에 의하여 생성되고 관리하는 자바 객체를 뜻하며, 하나 이상의 빈(Bean)을 관리한다.

  • Spring Framework에서는 ApplicationContext.getBean() 등의 메서드로 직접 자바 객체를 가져올 수 있다.
  • IntelliJ 에서는 Bean 인 경우 아래 사진처럼 왼쪽에 아이콘으로 표시된다.

스프링에서 스프링 빈을 인지하는 2가지 방법

  1. 스프링은 약속되어 있는 어노테이션이 붙어있는 클래스를 자동으로 인지한다. @Component어노테이션이 붙어있다면, 스프링이 자동으로 인지할 수 있게 된다.
    → 컴포넌트 스캔을 이용한 방식
  2. Bean Configuration File에 직접 Bean을 등록하면 스프링이 인지할 수 있게 된다.
    → 자바 코드로 직접 스프링 빈으로 등록하는 방식

✏️ 컴포넌트 스캔과 자동 의존관계 설정

컴포넌트 스캔

@Component어노테이션이 있으면 스프링 빈으로 자동 등록된다.

  • @Component를 포함하는 다음 애노테이션도 스프링 빈으로 자동 등록된다.
    • @Controller
    • @Service
    • @Repository

Spring 프로젝트에서 (프로젝트명)Application class를 @SpringBootApplication 어노테이션이 붙어있음을 확인할 수 있다.

이를 정의한 곳을 확인해보면

@ComponentScan 어노테이션이 붙어 있음을 확인할 수 있다.

이 어노테이션은 어느 지점부터 컴포넌트를 찾아보라고 알려주는 것이다. 즉, 이 어노테이션을 사용한 @SpringBootApplication 어노테이션이 PlaylistPackApplication 클래스에 붙어있으므로 이 클래스의 하위 모든 패키지를 훑어보면서 @Component 어노테이션을 활용한 모든 클래스를 찾고, Bean으로 등록한다.


DI (Dependency Injection), 의존성 주입

객체 의존 관계를 외부에서 넣어주는 것

  • 의존성 주입은 반드시 Bean으로 등록된 객체들 끼리만 가능하다. ( 컨테이너에 Bean을 등록되어 있지 않은 객체는 의존성 주입을 해주지 않음)
  • 생성자에 @Autowired 를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입한다.

의존성 주입 방법

  • 필드주입
    • 코드가 간결하다

    • 하지만, 클래스 외부에서 접근이 불가능해 테스트하기 어렵다는 단점이 있다.

    • 프레임워크에 의존적이고 객체지향적으로 좋지 않다.

      @Service
      public class StationFieldService {
          @Autowired
          private StationRepository stationRepository;
      
          public String sayHi() {
              return stationRepository.sayHi();
          }
      }
  • setter 주입
    • 변경 가능성이 있는 의존 관계에 사용한다.

    • 생성자 호출 이후에 필드 변수에 변경이 일어나야 하므로, stationRepository 변수에 final 제어자를 붙일 수 없다.

      @Service
      public class StationSetterService {
          private StationRepository stationRepository;
      
          @Autowired
          public void setStationRepository(StationRepository stationRepository) {
              this.stationRepository = stationRepository;
          }
      
          public String sayHi() {
              return stationRepository.sayHi();
          }
      }
  • 생성자 주입
    • 생성자를 호출할 때 딱 1번만 호출되기 때문에 stationRepository 변수를 final로 관리할 수 있다.

    • 생성자 주입 사용 시, 생성자가 1개인 경우 @Autowired를 생략할 수 있다.

      @Service
      public class StationConstructorService {
          private final StationRepository stationRepository;
      
          @Autowired
          public StationConstructorService(final StationRepository stationRepository) {
              this.stationRepository = stationRepository;
          }
      
          public String sayHi() {
              return stationRepository.sayHi();
          }
      }

순환참조를 방지할 수 있고 final로 선언하여 불변성을 보장할 수 있으며 테스트에 용이한 생성자 주입을 권장한다


회원 컨트롤러에 의존관계 주입 예제 코드

package hello.hellospring.controller;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {

    private final MemberService memberService;

    @Autowired
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }

}

참고: 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다(유일하게 하나만 등록
해서 공유한다) 따라서 같은 스프링 빈이면 모두 같은 인스턴스다. 설정으로 싱글톤이 아니게 설정할 수 있지만,
특별한 경우를 제외하면 대부분 싱글톤을 사용한다.


✏️ 자바 코드로 직접 스프링 빈 등록하기

  • @Configuration을 이용하면 Spring Project 에서의 Configuration 역할을 하는 Class를 지정할 수 있다.
  • Bean 으로 등록하고자 하는 Class에 @Bean 어노테이션을 사용해주면 간단하게 Bean을 등록할 수 있다.

자바 코드로 스프링 빈 등록 예제 코드

package hello.hellospring;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

0개의 댓글