클래스 간의 의존 관계를 설정하는 방법 중 스프링 빈을 직접 등록하는 방법을 적용해보자.
먼저 컴포넌트 스캔 방법에서 적용했던 @Autowired, @Service, @Repository 어노테이션을 모두 제거한다.
📁 src > main > java > hello.hello_spring > SpringConfig.java
package hello.hello_spring;
import hello.hello_spring.Service.MemberService;
import hello.hello_spring.repository.MemberRepository;
import hello.hello_spring.repository.MemoryMemberRepository;
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();
}
}
HelloSpringApplication
과 같은 위치에 파일을 하나 생성한 후, @Configuration 어노테이션을 통해 memberService와 memberRepository 가 빈에 등록되도록 한다.
또한 MemberService 생성자는 매개변수로 MemberRepository 객체를 받기 때문에, 스프링 빈에 등록된 memberRepository를 이용해 DI가 적용되도록 한다.
여기까지 따라왔다면, 컴포넌트 스캔 방식을 적용했을 때와 마찬가지로 위 의존 관계가 성립된다.
package hello.hello_spring.controller;
import hello.hello_spring.Service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
private final MemberService memberService;
// Autowired: MemberController 클래스 생성 시
// 스프링빈에 등록되어 있는 MemberService 객체를 가져다 넣어줌. (DI)
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
여기서 컨트롤러는 어차피 스프링이 관리해야하므로, 컴포넌트 스캔 방식을 적용했을 때와 마찬가지로 @Controller, @Autowired 어노테이션을 사용한다.
실무에서 주로 정형화된 컨트롤러, 리포지토리, 서비스는 컴포넌트 스캔 방식을 사용하고, 정형화되지 않거나 상황에 따라 구현 클래스를 변경해야 하는 경우에는 설정을 통해 스프링 빈으로 등록한다.
이 프로젝트에서는 추후 구현체를 바꿀 예정이므로, 자바 코드로 직접 설정해 스프링 빈으로 등록한 후, 변경이 필요할 때 📁 SpringConfig.java 파일 내 memberRepository()의 반환값만 변경해주면 된다.
클래스 간 의존 관계를 설정하며, 스프링 빈에 이미 등록되어 있는 객체를 생성자의 매개변수를 통해 주입하는 것을 생성자 주입
이라고 한다.
➡️ 지금까지 계속 활용해온 방식이자, 가장 추천되는 방식❗️
@Autowired private MemberService memberService;
클래스 내 생성자를 따로 만들지 않고, 스프링 빈에 이미 등록되어 있는 객체를 @Autowired 어노테이션을 통해 클래스의 필드에 주입하는 것을 필드 주입
이라고 한다.
@Autowired
public void setMemberService(MemberService memberService) {
this.memberService = memberService;
}
클래스에서 setter를 구현해, 스프링 빈에 이미 등록되어 있는 객체를 클래스의 필드에 주입하는 것을 setter 주입
이라고 한다.
setter 주입
의 단점은 setter가 public하기 때문에, MemberController를 호출하면서 setter를 통해 필드값이 변경되어 문제가 생길 가능성이 있다는 것이다.
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
memberService.setMemberRepository();
}
예를 들어 위와 같이 MemberService 클래스에 setter 주입
을 적용했을 경우, 컨트롤러에서 memberService 객체를 통한 리포지토리 접근이 가능해진다.