티스토리 뷰
다양한 DI 방법
- 생성자
- Setter
- 필드
- 일반 메소드
- 생성자 주입 방식 (요즘 가장 많이 쓰는 방식)
- 불변(final)이면서 필수인 의존관계에서 주로 사용
- 생성자 1개만 있다면 Autowired를 생략해도 주입됨(Bean, Component 방식 모두 해당)
- final 필드를 this로 접근 가능한 생성자 사용
- 수정자 Setter 주입
- 선택, 변경이 가능성이 있는 의존관계에서 주로 사용
- Autowired의 기본 동작은 주입할 대상이 없으며 오류가 발생하고, 주입할 동작이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정하면 된다.
- 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법
- 자바빈 프로퍼티 규약이란 필드를 외부에서 접근할 때 Getter, Setter로 접근하자는 규칙이 있는 규약이다.
- 선택, 변경이 가능성이 있는 의존관계에서 주로 사용
- 필드 주입 (안쓰는 것이 바람직)
- private 필드에도 가능(웬만하면 필드는 private를 쓰는 것이 당연)
- DI프레임워크가 없으면 아무것도 할 수 없음
- 코드가 꼬이고 오류가 날 확률이 매우 높음
- 테스트 코드 작성할 때는 가끔 쓰일 때 있음
- 일반 메소드 주입
- 한번에 여러 필드를 주입할 수 있다.
- 자주 사용하지 않는다.
옵션 처리
@Autowired(required = false)
public voidsetNoBean1(Member noBean1){
System.out.println("noBean1 = " + noBean1);
}
@Autowired
public voidsetNoBean2(@Nullable Member noBean2){
System.out.println("noBean2 = " + noBean2);
}
@Autowired
public voidsetNoBean3(Optional<Member> noBean3){
System.out.println("noBean1 = " + noBean3);
}
- requried = false
- 메소드 자체가 호출이 안됨
- @Nullable
- null이 들어감
- Optional<> (자바 8 문법) → 모던 자바 인 액션 책 공부하기!
- Optional로 감싸주어 위 예시의 경우 Optional.empty라는 값이 출력됨
- 위 옵션들을 생성자 자동 주입에서 일부에만 사용해 널값을 방지할 수 있음
생성자 주입이 가장 바람직한 이유
- 불변
- 대부분의 의존관계는 변하지 않고, 변하지 않는 것이 좋음
- 수정자 주입은 변경 가능하게 메소드를 열어 놓는 것 자체가 좋지 않음
- 오류를 찾아내기 수월하다
- final을 활용할 수 있기도 하고, final을 활용하면 생성자에서의 코드의 누락을 방지할 수 있어 오류를 찾기 더 쉽다.
- 프레임워크에 의존하지 않고 순수 자바를 활용할 수 있다는 것이다.
Lombok과 최신 트렌드
- 롬복 = 라이브러리
import lombok.*;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class HelloLombok {
private String name;
private int age;
}
- 위와 같이 어노테이션만 쓰면 게터 세터 생성자를 자동으로 생성해줘 바로 사용 가능하다.
- AllArgs는 모든 필드를 받아와 초기화하는 생성자라는 뜻이고, 당연히 NoArgs는 다 없는 생성자라는 뜻이다.
- 다음 두 코드는 같은 기능을 한다.
- Autowired와 롬복의 RequiredArgsConstructer 중 무엇을 쓰는지가 차이이다.
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired // 생성자 하나이면 @Autowired 생략 가능
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
public MemberRepository getMemberRepository(){
return memberRepository;
}
}
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
public MemberRepository getMemberRepository(){
return memberRepository;
}
}
- 밑의 코드가 더 간단하지만 같은 결과가 나온다.
- 최근에는 밑의 방식을 더 많이 사용한다.
조회 빈이 2개 이상인 문제
- 이름이 다르고 같은 타입의 빈이 여러개 등록되면 오류가 난다.
- 구현체가 두 개 이상인데 모두 스프링 빈으로 선언할 때를 말함
- 구현체(하위 타입)를 빈에 직접 등록하면 DIP를 위배하는 방법이 되기에 좋은 방법이 아니다.
조회 빈이 2개 이상일 때 해결 방법
- @Autowired에 필드 명을 매칭
- @Qualifier → @Qualifier끼리 매칭 → 빈 이름 매칭
- @Primary 사용
- Autowired를 쓸 때 타입은 상위 클래스, 이름은 하위클래스의 이름을 사용
- Autowired는 타입 매칭 후 타입 매칭의 결과가 2개 이상일 경우 필드 명, 파라미터 명으로 빈 이름을 매칭한다.
- @Qualifier를 파라미터 앞에 붙여 사용
- 사용할 구현체 클래스 위와 자동주입할 때 사용하는 생성자의 파라미터 앞에서 사용
- @Qualifier는 @Qualifier끼리 찾는 용도로만 사용하는 것이 좋음
- Qualifier끼리 매칭
- 없으면 빈 이름 매칭
- 그래도 없으면 예외 던짐
- @Primary를 사용할 구현체 클래스 위에 붙여 사용
- 가장 많이 쓰임 → 편리하고 명확함
- 스프링의 우선순위는 자세한 것 부터인 경우가 많다
- @Qualifier와 @Primary를 같이 사용할 경우 @Qualifier가 우선순위를 가짐
애노테이션 직접 만들기
- @Qualifier사용시 문자를 쓰는데 문자는 컴파일시 타입 체크가 안된다.
- 이를 해결하기 위해 애노테이션을 직접 만들어 쓸 수 있다.
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
→ 위와 같이 작성한 후 @MainDiscountPolicy를 활용하면 @Qualifier(”mainDiscountPolicy”) 와 같은 의미를 가진다.
→ 다른 애노테이션들도 재정의할 수 있다. 웬만하면 스프링이 제공하는 애노테이션을 쓰는 것이 낫지만 Qualifier와 같은 경우 활용하면 좋을 수 있다.
'학습 내용 > 학습 정리' 카테고리의 다른 글
MySQL의 Join 기법 (2) | 2024.01.11 |
---|---|
Database Key (0) | 2024.01.11 |
스프링 - 2022-4-13 (0) | 2022.04.13 |
HTTP - 2022-4-13 (0) | 2022.04.13 |
HTTP - 2022-3-30 (0) | 2022.03.30 |