DI : Dependency Injection (의존성 주입)
의존성 주입은 객체 간의 의존성을 줄이고 유지보수성을 높이기 위해 사용된다.
Spring 컨테이너에 여러 컴포넌트를 Bean으로 등록하여 생명주기 관리를 위임하고, 이 Bean으로 등록한 객체를 주입받아서 사용할 수 있다. DI 덕분에 개발자가 객체 생성에 대한 부분을 신경 쓰지 않고 비즈니스 로직에만 집중할 수 있게 된다.
의존성 주입 방법
의존성 주입 방법은 4가지가 있다.
- 생성자 주입
- Setter 주입
- Field 주입
- 일반 메서드 주입
이 중, 생성자 주입을 가장 많이 사용하고 Spring에서 권장하는 방법이다.
생성자 주입
생성자를 통해 의존성을 주입하는 방법이다.
- 생성자 호출 시점에 딱 한 번만 호출되는 것이 보장된다.
- 불변, 필수 의존관계에 사용된다.
@Component
public class PostServiceImpl implements PostService{
private final MemberRepository memberRepository;
private final TagRepository tagRepository;
@Autowired
public PostServiceImpl(MemberRepository memberRepository, TagRepository tagRepository){
this.memberRepository = memberRepository;
this.tagRepository = tagRepository;
}
// ...
}
생성자가 딱 1개만 있을 경우 @Autowired를 생략해도 자동으로 주입된다.
@RequiredArgsConstructor 롬복 어노테이션을 사용하면 private final로 지정된 필드에 대해 컴파일 시점에 생성자 주입 코드를 자동으로 생성해 준다.
@RequiredArgsConstructor
@Component
public class PostServiceImpl implements PostService{
private final MemberRepository memberRepository;
private final TagRepository tagRepository;
// ...
}
Setter 주입
자바 빈 프로퍼티 규약의 setter 메서드를 이용하여 의존성을 주입하는 방법이다.
- 선택, 변경 가능성이 있는 의존관계에 사용한다.
@Component
public class PostServiceImpl implements PostService{
private MemberRepository memberRepository;
private TagRepository tagRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
@Autowired
public void setTagRepository(TagRepository tagRepository){
this.tagRepository = tagRepository;
}
// ...
}
필드 주입
필드에 직접적으로 주입하는 방법이다.
- 코드가 간결해서 좋지만, 단점이 확실해서 사용하지 않는다.
- 외부에서 변경이 불가능해서 테스트하기 힘들다는 단점이 있다.
- DI 컨테이너가 없으면 아무것도 할 수 없다.
@Component
public class PostServiceImpl implements PostService{
@Autowired
private MemberRepository memberRepository;
@Autowired
private TagRepository tagRepository;
// ...
}
일반 메서드 주입
일반적인 메서드를 이용해서 주입할 수도 있다.
- 한 번에 여러 필드를 주입할 수 있다.
- 일반적으로 잘 사용되지 않는다고 한다.
@Component
public class PostServiceImpl implements PostService{
private MemberRepository memberRepository;
private TagRepository tagRepository;
@Autowired
public void init(MemberRepository memberRepository, TagRepository tagRepository){
this.memberRepository = memberRepository;
this.tagRepository = tagRepository;
}
// ...
}
생성자 주입을 사용하는 이유
Spring에서 공식적으로 생성자 주입을 권장한다.
대부분의 의존성 주입은 한번 일어나면 애플리케이션 종료시점까지 의존관계를 변경할 일이 없다.
오히려 대부분의 의존관계는 애플리케이션 종료 전까지 변하면 안 된다.
생성자 주입은 객체를 생성할 때 딱 1번만 호출되므로 이후에 호출되는 일이 없고, 이후에 수정할 수도 없다.
따라서 불변하게 설계할 수 있다.
- 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법이다.
- 기본적으로 생성자 주입을 사용하고, 필수값이 아닌 경우에는 setter 주입 방식을 옵션으로 부여한다.
- 필드 주입은 사용하지 않는 게 좋다.
필드 주입의 단점
- 객체를 한번 주입하면 외부에서 수정이 불가하다.
- 따라서 테스트 코드를 작성할 때 객체를 수정할 수 없다.
setter 주입의 단점
- 의존성 주입을 받는 객체의 변경이 가능하다. (public)
- 다른 곳에서 임의로 객체를 변경할 수 있다.
생성자 주입의 장점 - 객체의 불변성
- 생성자는 객체 생성 시 1회만 호출된다는 게 보장된다.
- 또한, 주입 받을 객체를 private final로 설정하며 변경할 수 없도록 한다.
테스트 코드 작성의 편리함
- 필드 주입의 경우 외부에서 필드를 수정할 수 없어서, DI 프레임워크 없이 Java 코드로 테스트가 불가하다.
컴파일 시 오류 확인 가능
- 생성자 주입을 사용하면 컴파일 시점에 에러를 확인할 수 있다.
- A가 B 객체를 참조하고, B가 A를 참조하는 순환 참조 오류 발생 시에도 컴파일 에러가 뜬다.
- 그러나 생성자 주입이 아닌 다른 주입 방식 선택 시 StackOverFlow 에러가 발생한다.
위와 같은 여러가지 이유로 생성자 주입을 권장하고, 대부분 생성자 주입을 이용한다고 한다.
반응형
'JAVA & Spring > Spring' 카테고리의 다른 글
[Spring] 빈 스코프 (Bean Scope) / 싱글톤, 프로토타입 등 (0) | 2024.02.27 |
---|---|
[Spring] 빈 생명 주기 콜백 (Bean LifeCycle) @PostConstruct / @PreDestroy (0) | 2024.02.23 |
[Spring Boot] @RequestMapping 매핑 (0) | 2023.10.02 |
[Spring] application.properties와 application.yml의차이 (0) | 2023.08.18 |
[Spring] DB 연동 (MariaDB), MyBatis 사용 (0) | 2023.08.14 |