Spring

[Spring] 스프링 빈과 의존관계

lumana 2024. 4. 29. 03:40

스프링 빈과 의존관계

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

  • 회원 컨트롤러가 회원서비스와 회원 리포지토리를 사용할 수 있게 의존관계를 준비하자

회원 컨트롤러에 의존관계 추가

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 = new MemberService();
		// 위 방식 보단 아래의 방식이 더 권장됨(생성자 주입) : 여러군대에서 memberService를 사용하기 때문

		/*
		AutoWire를 통해 스프링이 스프링 컨테이너에 등록된 memberService를 가져옴(연결해줌)
		autowired가 컨트롤러, 서비스, 리포지토리의 연관관계를 연결해주기 때문
		오류가 나는 이유 : memBerService를 스프링이 관리하지 않기 때문(순수한 자바 코드임)
		구현체에 @Service를 추가해줘서 Spring container에 멤버 서비스를 등록해줘야 한다
		Repository도 구현체에 @Repository를 추가해줘야 한다
		위와 같은 방식을 컴포넌트 스캔 방식이라고 함 (@service, @Repository 안에 컴포넌트라는 anotation이 있기 때문에)
		@Component 애노테이션이 있으면 스프링 빈으로 자동으로 등록 된다.
		hello.hellospring에 있는 모든 패키지를 스캔해서 스프링 빈으로 등록해줌(하위 패키지가 아닌 얘들은 기본적으로 스캔이 안됨, 설정을 따로 해줘야 함)
		Service, Repository, Controller는 정형화된 패턴임
		컨트롤러를 통해서 외부 요청을 받고, 그 다음에 서비스에서 비즈니스 로직을 만들고, 리포지토리에서 데이터를 저장하는 패턴
		스프링 컨테이너에 스프링 빈을 등록할 때 기본적으로 싱글톤으로 등록한다. (설정으로 바꿀 수 있음)

		생성자 주입 : 생성자를 통해서 주입이 되는 거

		필드 주입 : 별로 안좋음
		@Autowired private MemberService memberService;
		생성될 때 autowired되고, 중간에 바꿔치기할 방법이 없음

		Setter 주입 : 멤버 컨트롤을 호출했을 때 public으로 열려있어야 함
		중간에 바꿔치기 할 이유가 없는데도 public으로 노출이 되는 문제가 있음
		중간에 잘못 바꾸면 문제가 생김
		@Autowired
		public void setMemberService(MemberService memberService) {
				this.memberService = memberService;
		


		요즘 권장하는 방식은 생성자 주입
		멤버 컨트롤러가 생성이 될 때 스프링 빈에 등록되어 있는 멤버 서비스 객체를 가져다가 넣어줌(Dependency Injection)
		*/
    private final MemberService memberService;


    @Autowired
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}
  • 생성자에 @Autowired 가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI (Dependency Injection), 의존성 주입이라 한다.
  • 이전 테스트에서는 개발자가 직접 주입했고, 여기서는 @Autowired에 의해 스프링이 주입해준다.
  • 그냥 위 코드를 실행하면, memberService가 스프링 빈으로 등록되어 있지 않아 오류가 발생함

컴포넌트 스캔원리

  • @Component 애노테이션이 있으면 스프링 빈으로 자동 등록된다.
  • @Controller 컨트롤러가 스프링 빈으로 자동 등록된 이유도 컴포넌트 스캔 때문이다.
  • @Component 를 포함하는 다음 애노테이션도 스프링 빈으로 자동 등록된다.
    • @Controller
      • @Service
      • @Repository
      • @Controller, @Service, @Repository 안을 봐보면 Component가 있음
@Service

public class MemberService {
    private final MemberRepository memberRepository;
    @Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

@Repository
public class MemoryMemberRepository implements MemberRepository {

 

  • memberServicememberRepository 가 스프링 컨테이너에 스프링 빈으로 등록되었다.
  • 참고) 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본적으로 싱글톤으로 등록한다(물론 설정으로 바꿀 수 있긴 함). 따라서 같은 스프링 빈이면 모두 같은 인스턴스인 것이다.

스프링 빈을 등록하는 2가지 방법

  • 컴포넌트 스캔과 자동 의존관계 설정 (위에서 했던 방식)
  • 자바 코드로 직접 스프링 빈 등록하기

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

  • 회원 서비스와 회원 리포지토리의 @Service, @Repository, @Autowired 애노테이션을 제거하고 진행 한다.
package hello.hellospring.service;

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 spring bean에 등록할거야
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

지금 데이터 저장소가 선정되지 않았던 시나리오에 의해 인터페이스를 설계하고 메모리 멤버 리포지토리를 쓰고 있었음
근데 나중에 이거를 다른 리포지토리로, 기준에 운영 중인 코드를 하나도 손대지 않고 바꿔치기 할 수 있는 방법이 있음
이거를 하려면 구현체 바꿔치기를 해야 한다. 상황에 따라 구현 클래스를 변경해야하면 설정을 통해서 스프링 빈으로 등록하는게 좋음
DB에 연결한다면, SpringConfig에서 MemberRepository() 대신 DbMemberRepository()를 리턴하면 됨

  • 참고)
    • XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략한다.
    • DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
    • 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다 (위의 데이터 저장소의 경우)
    • @Autowired 를 통한 DI는 helloController , memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.

참조) 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강좌 (인프런 김영한)