[Spring] 김영한 스프링 입문_Section 4

dO_the_Jeegu·2023년 2월 5일

▶ 스프링 빈과 의존관계

  1. 컴포넌트 스캔과 자동 의존관계 설정
  2. 자바 코드로 직접 스프링 빈 등록하기
  3. 의존성 주입 방식

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

: MemberControllermemberServicememberRepository를 사용할 수 있도록 의존관계를 설정한다.

(1) MemeberController 추가

package hello.hellospring.controller;

import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

//Spring Container에 MemberController 객체를 넣고 Spring이 관리
// => Spring Bean이 관리된다고 말함
@Controller
public class MemberController {

    private final MemberService memberService;

    //MemberService 객체를 하나 생성하고 여러 Controller에서 공용으로 쓰게 하기
    //Spring Container한테 등록하고 사용하면 됨 => 생성자로 연결
    //필요한 의존 객체의 타입에 해당하는 Bean을 Spring Container에서 찾아서 자동으로 주입
    @Autowired //Controller와 Service 연결
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}

(2) memberService, memoryMemberRepository 수정

: 이때 MemberSercive classMemoryMemberRepository class에서도 애노테이션 설정 필요

  • memberService
package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;


@Service // Client의 요청에 대한 비즈니스 로직을 수행. @Component의 하위 애노테이션
public class MemberService {

    private final MemberRepository memberRepository;

    //DI(Dependency Injection) 의존성 주입
    @Autowired
    public MemberService(MemberRepository memberRepository) { 
        this.memberRepository = memberRepository;
    }

    //(...생략...)
}
  • memoryMemberRepository
package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.stereotype.Repository;

import java.util.*;

@Repository // @Component의 하위 애노테이션
public class MemoryMemberRepository implements MemberRepository{
    //(...생략...)
}

❗ 추가 설명

  • @Component : Bean 객체를 만드는 역할. 개발자가 직접 컨트롤 할 수 있는 class에서 사용한다. @Component의 구체화된 형태로 @Repository, @Service, @Controller가 있다.
    • Bean이란? Spring Container에 등록되어 관리되는 객체
    • Spring Container에 Spring Bean을 등록할 때 기본적으로 싱글톤으로 등록한다.
      (유일하게 하나만 등록해서 공유)
      => 특별한 경우를 제외하고, 같은 Spring Bean이면 모두 같은 인스턴스.

  • 생성자에 @Autowired가 있으면 스프링이 Spring Container에서 연관된 객체를 찾아서 넣어준다(주입).

  • 객체 의존관계를 외부에서 넣어주는 것을 DI(Dependency Injection), 의존성 주입이라고 한다.

  • Spring Container = Spring Bean Container = Spring Bean 혼용

(3) 설계 및 동작 순서

  • memberControllermemberService가 필요하고 memberServicememberRepository가 필요함 => 의존 관계(Autowired) 설정
  • Spring이 memberService 객체를 생성할때 @Service를 확인하고 Spring Container에 등록하면서 생성자를 호출함
  • 그때 생성자에 @Autowired를 확인하고 memberRepository가 필요하다는 것을 체크, Spring Container에 있는 memberRepository를 주입
  • 여기서 memberRepository의 구현체는 memoryMemberRepository므로 이걸 memberService에 주입해줌

✍ 정리

  • 컴포넌트 스캔 : 스프링 구동 시 @component 관련 애노테이션이 있으면 모두 자동으로 Spring Container에 객체로 등록(=Spring Bean으로 등록)
  • 자동 의존관계 설정 : @AutoWired는 연관 관계(선)을 이어주는 역할

❓ 컴포넌트 스캔 범위

: 실행 Application이 속한 package를 포함한 하위 패키지의 class에서만 가능.

package hello.hellospring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HelloSpringApplication {

	public static void main(String[] args) {
		SpringApplication.run(HelloSpringApplication.class, args);
	}

}

위 코드의 경우 hello.hellospring 패키지 바깥의 class에서는 @component가 있어도 컴포넌트 스캔 안 함.


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

: MemberService와 MemeberRepository의 @Service, @Repository, @Autowired 애노테이션을 제거하고 진행한다.

(1) memberService, memoryMemberRepository 수정

: MemberSercive class와 MemoryMemberRepository class에서 애노테이션 삭제

  • memberController의 @Controller는 그대로 두기

(2) SpringConfig 추가

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 //Spring Bean을 수동으로 입력
    public MemberService memberService() { //Spring Container에 등록
        return new MemberService(memberRepository()); //생성자에서 MemberRepository를 넣어줘야함
    }

    @Bean
    public MemberRepository memberRepository() { //Spring Container에 등록
        return new MemoryMemberRepository(); //구현체
    }
}
  • 설계 및 동작 순서
    memberServicememberRepository를 둘다 Spring Container에 등록
    ② 등록 되어있는 memberRepositorymemberService에 주입

3. 의존성 주입 방식

(1) 생성자 주입

: 생성자를 통해 의존 관계 주입. 불변성 보장.

@Controller
public class MemberController {

    private final MemberService memberService;

    @Autowired //생성자에 @Autowired
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}

(2) 필드 주입

: 필드에 @Autowired 입력. 간단하지만 외부에서 접근 불가능 => 테스트하기 힘듦

@Controller
public class MemberController {

    @Autowired private MemberService memberService; //필드에 @Autowired

}

(3) 수정자 주입

: Setter를 통해 의존 관계 주입. 주입받는 객체가 변경될 가능성이 있는 경우 사용 (실제로는 극히 드묾)

@Controller
public class MemberController {

    private MemberService memberService;

    @Autowired //Setter를 생성하여 @Autowired 입력
    public void setMemberService(MemberService memberService) {
        this.memberService = memberService;
    }
}
  • XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략

💡💡💡 생성자 주입 방식을 권장

❗ 참고사항

  • 실무에서 주로 정형화 된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용. 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경 해야하면 설정을 통해 스프링 빈으로 등록.
  • @Autowired 를 통한 DI는 helloController , memberService 등과 같이 Spring Container에 올라간 객체에서만 동작. 스프링 빈으로 등록하지 않고 직접 생성한 객체(new)에서는 동작하지 않는다.
profile
오지는 갓생 살기

0개의 댓글