※ 본문은 인프런 김영한님의 스프링 강의를 바탕으로 학습한 내용을 기록한 글입니다.
오개념이 있다면 댓글로 알려주세요!
[ 1 ] 여러가지 DI 방법
(1) 생성자 주입
- 생성자 호출 시점에 딱 1번만 호출되는 것이 보장되므로 불변, 필수 의존관계에 사용하는 것이 좋다.
- 생성자가 1개만 있다면 @Autowired 를 생략할 수 있다.
[ Config에서 DI ]
@Bean
public MemberRepository memberRepository() {
return new MemberRepository();
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
[ 의존 자동주입 ]
@Component
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
(2) setter
- 자바 빈 프로퍼티 규약의 setter를 사용하여 DI를 할 수 있다.
- 의존 자동주입을 하려면 setter에 @Autowired 애노테이션을 설정하면 된다.
- 선택, 변경 가능성이 있는 의존관계에 사용할 수 있다.
[ 의존 자동주입 ]
@Component
public class MemberService {
private MemberRepository memberRepository;
@Autowired
public void setMemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
(3) 필드 주입
- 코드가 간결해지지만, 외부에서 변경이 불가능하기 때문에 테스트하기 힘들다는 단점이 있다.
- 테스트 환경에서 의존관계를 변경하려면 setter를 추가해야 한다.
- 즉, DI 프레임워크가 없으면 아무것도 할 수 없기 때문에 필드 주입을 지양하자.
[ 의존 자동주입 ]
@Component
public class MemberService {
@Autowired
private MemberRepository memberRepository;
}
(4) 일반 메서드 주입
- 생성자나 setter가 아닌 일반적인 메서드에도 @Autowired를 사용하여 의존 자동주입을 할 수 있다.
- 그러나 일반적으로 잘 사용하지 않는다.
[ 2 ] 생성자 주입을 선택해야 하는 이유
일반적으로 애플리케이션 종료시점까지 의존관계를 변경할 일이 없다. 오히려 대부분의 의존관계는 애플리케이션이 종료되기 전까지 불변해야 한다.
setter는 접근제어자가 public이기 때문에 누군가 실수로 의존관계를 변경하는 문제가 발생할 수 있다. 반면, 생성자 주입은 객체 생성 시 딱 1번만 호출되는 것이 보장되므로 이후에 호출될 일이 없다. 따라서 생성자 주입을 사용하는 것이 좋은 설계 방법이다.
또한, 생성자 주입을 사용하면 필드에 final 를 사용할 수 있으므로 생성자에서 값이 설정되지 않는 오류를 컴파일 시점에 막아준다는 이점이 있다. 즉, 컴파일 오류를 통해 DI를 누락하는 문제를 방지할 수 있는 것이다.
[ 3 ] @Autowired 의존 자동주입 및 주의점
@Autowired 를 활용하여 의존 자동주입을 하기 위해서는 주입할 객체가 스프링 컨테이너에 등록된 스프링 빈이어야 한다(@Autowired의 required 옵션 기본 값이 true이기 때문). 만약 주입할 객체가 등록된 스프링 빈이 아니라면 UnsatisfiedDependencyException 이 발생한다.
하지만, 스프링 빈이 아니더라도 의존 자동 주입을 동작하게 하는 세 가지 방법이 있다.
(1) @Autowired(required = false) : 주입할 객체가 등록된 스프링 빈이 아니라면 의존 주입 메서드 자체가 호출되지 않는다.
(2) @Nullable
@Component
public class MemberService {
@Autowired
public void setMemberService(@Nullable MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
만약 memberRepository가 등록된 스프링 빈이 아니라면 null이 입력된다.
(3) Optional<T>
@Component
public class MemberService {
@Autowired
public void setMemberService(Optional<MemberRepository> memberRepository) {
this.memberRepository = memberRepository;
}
}
만약 memberRepository가 등록된 스프링 빈이 아니라면 Optional.empty가 입력된다.
@Autowired 는 기본적으로 타입으로 조회하기 때문에 스프링 컨테이너에 같은 타입의 스프링 빈이 두개라면 NoUniqueBeanDefinitionException 이 발생한다.
이를 해결하는 세 가지 방법은 다음과 같다.
(1) @Autowired는 타입으로 조회한 후 스프링 컨테이너에 같은 타입의 스프링 빈이 여러개라면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다. 즉, 필드 이름과 파라미터 이름을 빈 이름으로 변경하면 특정 스프링 빈이 주입된다.
(2) 빈 등록 시 @Qualifier를 설정하고, 의존관계 주입 시에도 @Qualifier를 붙여준다.
@Component
@Qualifier("memoryMemberRepository")
public class MemberRepository {
...
}
@Component
public class MemberService {
@Autowired
public MemberService(@Qualifier("memoryMemberRepository") MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
(3) 같은 타입의 스프링 빈이 두 개일 때 의존 자동주입 할 스프링 빈에 @Primary 애노테이션을 추가한다.
※ @Qualifier가 @Primary 보다 우선순위가 높다.
[ 4 ] lombok
자바의 애노테이션 프로세서는 컴파일 시점에 미리 정의한 애노테이션의 소스코드를 분석하고 처리하며 소스코드(.java)나 바이트코드(.class)를 생성할 수 있다.
롬복은 이러한 애노테이션 프로세스를 활용하며 컴파일 시점에 생성자나 getter/setter, toString 등의 코드를 자동으로 생성해준다.
롬복을 활용하여 생성자 주입을 다음과 같이 할 수 있다.
@Service
@RequiredArgsConstructor
public class MemberService {
private final JpaMemberRepository memberRepository;
}
@RequiredArgsConstructor 애노테이션은 final이나 @Notnull 이 붙은 필드의 생성자를 자동으로 생성해준다.
'Programming > Spring' 카테고리의 다른 글
[Spring] 컴포넌트 스캔 (1) | 2023.03.24 |
---|---|
[Spring] 싱글톤 컨테이너 (1) | 2023.03.15 |
[Spring] 스프링 컨테이너(Spring Container) (1) | 2023.03.07 |