김찬진의 개발 블로그
23/02/13 [DI(Dependency Injection)] 본문
DI의 목적은 다양하다.
- 중복된(심지어 다를 수 있는) 객체 생성을 피하기 위함
- Spring Container로부터 객체를 가져다 쓰기 위함
DI의 방식은 3가지이다.
- 필드 주입 (△) - 다형성 없음
- setter 주입(△) - setter가 public하게 노출되므로 위험
- 생성자 주입(O) - 처음 Spring이 뜰 때 Spring Bean이 조립되고 이후엔 변경 못함
1. 중복된(심지어 다를 수 있는) 객체 생성을 피하기 위함
MemberService클래스와 MemberServiceTest클래스의 멤버변수인 MemoryMemberRepository 객체가 다른 객체일 가능성이 있습니다. 각각의 두 클래스에서 굳이 동일한(총 2개) MemoryMemberRepository객체를 만들 필요는 없습니다. 왜냐하면 두 객체가 다른 객체일 가능성도 있고 목적상 다른 객체이어야 할 이유가 전혀 없기 때문입니다. 이러한 문제를 개선하기 위해 DI가 필요합니다.
DI를 하는 방법은 다음과 같습니다.
MemberService클래스에서 멤버변수에 MemoryMemberRepository 객체를 생성하지 않고 참조변수만을 선언합니다. 생성자를 만들어 MemoryMemberRepository 객체를 주입받고 그 객체를 멤버변수 MemoryMemberRepository로서 사용하도록 합니다.
MemberServiceTest클래스에서 MemberService 생성자를 사용하여 MemberService 객체를 생성하는데 이 때, 생성자의 인자에 MemoryMemberRepository 객체를 생성해 주입합니다. MemberService클래스에서 생성자를 만들었고 생성자의 인자로 MemoryMemberRepository 객체를 주입받고 그 객체를 멤버변수로 사용하기로 했다는 것을 잊지말아야 합니다. 이것이 핵심입니다.
물론 14분대 강의영상에서는 MemberServiceTest클래스에서 MemberService의 각 메서드들이 테스트되기 전 매번 MemberService 객체가 만들어져야 하므로 beforeEach() 메서드 안에서 구현이 되었을 뿐, DI의 원리는 지금까지 설명한 바와 동일합니다.
2. Spring Container로부터 객체를 가져다 쓰기 위함
(1) 컴포넌트스캔과 자동 의존관계 설정
@Service, @Repository, @Controller 어노테이션을 사용해서 Spring Container에 Member객체(Spring Bean) 등록
@Service
public class MemberService {...}
@Repository
public class MemoryMemberRepository implements MemberRepository {...}
MemberController는 MemberService객체(Spring Bean)를 의존하기(연결해서 가져오기) 위해 멤버변수에 MemberService객체를 생성하지 않고 참조변수만을 선언하고 MemberController생성자에 @Autowired 어노테이션 사용하고 생성자의 인자에 MemberService객체를 주입(객체 생성하지 않음!). 객체를 생성하지 않고 참조변수만을 선언하는 이유는 앞서 말했듯 중복된 객체를 생성하지 않기 위해서임.
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
MemberService는 MemoryMemberRepository객체(Spring Bean)를 의존하기(연결해서 가져오기) 위해 멤버변수에 MemberRepository(다형성을 위한 구현체 참조타입이 아닌 인터페이스 참조타입) 객체를 생성하지 않고 참조변수만을 선언하고 MemberService생성자에 @Autowired 어노테이션 사용하고 생성자의 인자에 MemberRepository객체를 주입(객체 생성하지 않음!)
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
(2) 자바 코드로 직접 스프링빈 등록하기
@Repository, @Service 어노테이션을 사용하지 않고 @Controller 어노테이션만 사용하여 Spring Container에 객체(Spring Bean) 등록
MemberController 클래스에서는 MemberService객체(Spring Bean)를 의존하기(연결해서 가져오기) 위해 멤버변수에 MemberService객체를 생성하지 않고 참조변수만을 선언하고 MemberController생성자에 @Autowired 어노테이션 사용하고 생성자의 인자에 MemberService객체를 주입(객체 생성하지 않음!). 객체를 생성하지 않고 참조변수만을 선언하는 이유는 앞서 말했듯 중복된 객체를 생성하지 않기 위해서임.
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
Config 클래스에서는 @Configuration 어노테이션을 사용하여 멤버(필드, memberService(), memberRepository())들을 Spring Container에 객체(Spring Bean)로 등록시켜야 함을 인식함.
meberService 메서드(필드)에 @Bean 어노테이션을 사용하여 자신이 Spring Container에 객체(Spring Bean)로 등록되어야 함을 알려줌. 반환은 MemberService 생성자로 MemberService객체를 만들어 반환함.
meberRepository 메서드(필드)에 @Bean 어노테이션을 사용하여 자신이 Spring Container에 객체(Spring Bean)로 등록되어야 함을 알려줌. MemoryMemberRepository생성자로 MemoryMemberRepository객체를 만들어 반환함.
이 때 MemberService는 MemberRepository를 의존하므로 memberService메서드가 반환하는 MemberService 생성자에 memoryRepository메서드를 주입함
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
MemberService 클래스에서는 MemoryMemberRepository객체(Spring Bean)를 의존하기(연결해서 가져오기) 위해 멤버변수에 MemberRepository(다형성을 위한 구현체 참조타입이 아닌 인터페이스 참조타입) 객체를 생성하지 않고 참조변수만을 선언하고 MemberService생성자에 @Autowired 어노테이션 사용하지 않고 생성자의 인자에 MemberRepository객체를 주입(객체 생성하지 않음!)
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
'1일1배움 > Spring (김영한 님)' 카테고리의 다른 글
[23/04/04] extends와 객체 생성의 차이 (0) | 2023.04.04 |
---|---|
[23/04/04] 만약 이미 UserDao가 다른 목적을 위해 상속을 사용하고 있다면 어쩔 것인가? (0) | 2023.04.04 |
[23/03/31] 클래스 다이어그램 (0) | 2023.03.31 |
23/02/14 [Spring 동작 순서] (0) | 2023.02.14 |
23/02/13 [인텔리제이 단축키 정리] (0) | 2023.02.13 |