본문 바로가기
  • 기억의 유한함을 기록의 무한함으로✍️            예비 개발자가 꿈꾸는 공간 여기는 안나의 개발 블로그 💻
Programming/Spring

[Spring 입문하기] Section 04. 스프링 빈과 의존관계

by 제가안난데여♪(´ε`*) 2024. 1. 25.
728x90

✏️ 스프링 빈(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();
    }
}
반응형