스프링 입문 - Ch 4. 스프링 빈과 의존관계(2)

seren-dev·2022년 3월 28일
0

스프링 입문

목록 보기
7/11

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

컨트롤러 파일을 제외한 @Service, @Repostiory, @Autowired 지우자.

@Controller
public class MemberController {

    private final MemberService memberService;

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

이전과 똑같은 오류가 난다.

hello.hellospring 패키지 내에 SpringConfig 자바 클래스 파일을 만든다.

@Configuration
public class SpringConfig {

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}
  • 스프링이 뜰 때, 이 @Configuration을 읽고 @Bean을 읽고 memberService를 스프링 빈으로 등록, 이 로직을 호출해서 스프링 빈으로 등록
  • 멤버 서비스랑 멤버 리포지토리를 스프링 빈으로 등록
  • 스프링 빈에 등록되어 있는 memeberRepository를 멤버 서비스에 넣어준다.
  • 컨트롤러는 컴포넌트 스캔, Autowired 사용

참고: 과거에는 자바 코드말고 XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략한다.

참고: DI(의존성 주입)에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.

1. 필드 주입

@Controller
public class MemberController {
	@Autowired private MemberService memberService;
  • 동적으로 바꿀 방법이 없어서 안 좋음. 스프링 뜰 때만 넣어줌
  • 테스트 실행시 스프링 컨테이너의 도움 없이 MemberService가 가지고 있는 여러 Repository를 자유롭게 변경하면서 테스트 할 수 있어야 한다. 그런데 필드 주입을 사용하면, 스프링 컨테이너가 없을 때 의존하는 객체를 변경할 수 있는 방법이 없다.

2. setter 주입

@Controller
public class MemberController {
	private MemberService memberService;

    @Autowired
    public void setMemberService(MemberService memberService) {
        this.memberService = memberService;
    }
  • 생성 따로, 나중에 setter 호출 되서 주입 따로
  • final 못 씀
  • public하게 노출 되어있음 -> 바꿀 일이 없는데 잘못 바꿀 수 있음
  • 처음 어플리케이션이 조립될 때 빼고 변경을 못하도록 막아야 함

3. 생성자 주입

@Controller
public class MemberController {

    private final MemberService memberService;

    @Autowired
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}
  • 처음 애플리케이션이 조립될 때, 한 번 주입되고 끝남
    ex) 생성자를 통해 멤버 서비스가 멤버 컨트롤러에 주입됨.
  • 생성자 주입에서는 스프링 컨테이너의 도움 없이 직접 new MemberService(new XxxRepository)와 같은 식으로 스프링 컨테이너의 도움 없이 원하는 객체를 변경해서 테스트 하거나 실행할 수 있습니다.

참고: 생성자 주입을 @Autowired를 사용하는 필드 주입보다 권장하는 하는 이유

참고: 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
-> SpringConfig 파일에서 @Bean public MemberRepository memberRepository() { return new DBMemberRepository(); }로만 바꿔주면 된다.

주의: @Autowired 를 통한 DI는 helloConroller , memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
ex) MemberService를 빈으로 등록하지도 않았는데 MemberService 클래스 안에서 @Autowired를 써도 안 먹힘.

의존성을 주입한다는 이유는 객체간의 결합도를 낮추기 위함이다.
그럼 왜 결합도를 낮추어야 하는가? 라고 질문을 해보면, 각 객체는 비즈니스 요구사항에 의해 계속 변하기 때문인데, 그럼 의존성 주입을 해야하는 객체는 대체로 변화할 여지가 있는 객체라고 말할 수 있다.
그러나 컨트롤러는 사용자의 URL을 특정 서비스에 전달하는 매퍼의 역할을 할 뿐 비즈니스 로직을 가지고 있지 않다. 그래서 비즈니스 요구사항이 변한다고 해서 컨트롤러를 수정하거나 다른것으로 교체해야 하는 경우는 없다. 이런 이유 떄문에 컨트롤러 자체를 Config로 관리하지 않는다고 생각한다.

스프링 컨테이너가 직접 의존 관계를 처리해야 한다면 @Autowired를 명시해야 하며 이 때 두 객체(의존성을 주입받는 객체와 주입할 객체) 모두 스프링 빈이어야 합니다.
@Autowired로 의존성을 주입하려 할 때는 둘 다 스프링 빈이어야 하나 사용자가 집적 의존관계를 주입해준다면 주입할(주입받는) 객체는 빈이 아니어도 괜찮습니다.

@Bean이 붙은 메서드에 대해서는 메서드 이름이 빈의 이름이 된다.


정리

어노테이션을 활용할 경우

  1. 스프링 컨테이너가 동작할때 먼저 컴포넌트 스캔 진행
  2. @Component 어노테이션이 달린 클래스를 찾아 빈으로 등록(Service, Controller, Repository 등)
  3. @Autowired을 찾아 의존성 주입을 수행함
  4. MemberControllerMemberService@Autowired로 되어있으므로 컨테이너에에 빈으로 올라가있는 MemberService 주입
  5. MemberServiceRepository부분이 @Autowired이므로 컨테이너에 빈으로 올라간 Repository 주입

=> 생성자 주입의 경우 생성 되는 과정 속에서 의존관계 주입이 발생

springConfig를 통해 자바코드로 할 경우

  1. 컴포넌트 스캔이 진행됨. @Configuration어노테이션에 @Component가 포함되어 있음
  2. springConfig를 통해 memberService, memberRepository가 Bean으로 등록됨
  3. memberServicereturn new MemberService(memberRepository());로 인해 MemberServiceMemberRepository는 의존성이 존재하고, MemberServiceMemberRepository를 주입해 줌
  4. MemberController 등록
  5. @Autowired를 통해 memberService를 주입해야 함
  6. 컨테이너에 MemberService 빈이 올라가 있으므로 그것을 주입

=> MemberController의 경우 MemberService가 빈으로 등록되어 있어도 @Autowired는 필요합니다. (물론, 스프링 부트의 경우 생성자 주입에 대하여 편의를 위해 @Autowired를 생략할 수 있도록 구현되어 있습니다.) 스프링이 @Autowired를 보고 의존관계를 자동 주입(컨테이너에서 의존관계에 해당하는 빈을 찾아와서 주입)하기 때문입니다.

0개의 댓글