코드 그라데이션
다양한 의존관계 주입 방법 본문
다양한 의존관계 주입 방법
의존관계 주입은 크게 4가지 방법이 있다
생성자 주입
지금까지 했던 것.
이름 그대로 생성자를 통해서 의존 관계를 주입 받는 방법이다.
특징
- 생성자 호출시점에 딱 1번만 호출되는 것이 보장된다.
- 불변, 필수 의존관계에 사용
(생성자는 다 포함시켜야 한다.) 안그러면 빨간줄
외부에서 수정할 수가 없고,
들어오고 나면 인스턴스를 수정할 방법이 없다.
(버그 생길 확률이 줄어듦)
나는 공연하고 끝내고 싶은데, 중간에 새로운 배우를 투입시키면 그럴 수가 없는 상황.
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
중요! 생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입 된다. 물론 스프링 빈에만 해당한다.
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
이 값을 찍어보면
값 잘 들어옴을 확인할 수 있음.
수정자 주입(setter 주입)
setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법이다.
특징
- 선택, 변경 가능성이 있는 의존관계에 사용
- 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다.
-> 생성자에서 주입할 때는 이 파라미터들이 필수값이라고 했는데,
set 메서드는 의존관계 주입이 안 되었을 수도 있다. 그럴 때 선택적으로 의존관계를 줄 수도 있다.
이 때는 필수값 아니라는 표시를 아래처럼 해주면 된다.
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
참고
: @Autowired 의 기본 동작은 주입할 대상이 없으면 오류가 발생한다.
주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정하면 된다.
참고
: 자바빈 프로퍼티,
자바에서는 과거부터 필드의 값을 직접 변경하지 않고,
setXxx, getXxx 라는 메서드를 통해서 값을 읽거나 수정하는 규칙을 만들었는데, 그것이 자바빈 프로퍼티 규약이다.
더 자세한 내용이 궁금하면 자바빈 프로퍼티로 검색하면 된다.
이렇게 찍어보면
요 순서로 찍혀 나온다.
여기 memberRepository와 discountPolicy는 다 똑같은 값이 의존관계 주입 된다.
자바빈 프로퍼티 규약 예시
class Data {
private int age;
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
필드 주입
- 이름 그대로 필드에 바로 주입하는 방법이다.
특징
- 코드가 간결해서 많은 개발자들을 유혹하지만 외부에서 변경이 불가능해서 테스트 하기 힘들다는 치명적인 단점이 있다. -> 옛날에 많이 썼다. 지금은 거의 안 쓴다. 안티패턴
- DI 프레임워크가 없으면 아무것도 할 수 없다.
사용하지 말자!
- 애플리케이션의 실제 코드와 관계 없는 테스트 코드
- 스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
테스트 이렇게 찍어보면
이렇게 찍혀 나온다.
OrderServiceTest 에도 테스트를 추가해서 찍어보면
@Bean
OrderService orderService(MemberRepository memberRepoisitory, DiscountPolicy discountPolicy) {
new OrderServiceImpl(memberRepository, discountPolicy)
}
NPE 터진다.
그래서 내가 값을 넣어주고 싶은데, 넣을 방법이 없다.
-> setter 열어줘서 setMemberRepository, setDiscountPolicy를 넣어줘야 해결 가능하다.
이렇게 말이다.
참고
: 순수한 자바 테스트 코드에는 당연히 @Autowired가 동작하지 않는다.
@SpringBootTest 처럼 스프링 컨테이너를 테스트에 통합한 경우에만 가능하다.
참고
: 다음 코드와 같이 @Bean 에서 파라미터에 의존관계는 자동 주입된다.
수동 등록시 자동 등록된 빈의 의존관계가 필요할 때 문제를 해결할 수 있다.
@Bean
OrderService orderService(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
new OrderServiceImpl(memberRepository, discountPolicy);
}
필드 주입은 그냥 쓰지 마세요,,,,,
일반 메서드 주입
- 일반 메서드를 통해서 주입 받을 수 있다.
특징
- 한번에 여러 필드를 주입 받을 수 있다.
- 일반적으로 잘 사용하지 않는다.
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
참고
: 어쩌면 당연한 이야기이지만 의존관계 자동 주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다.
스프링 빈이 아닌 Member 같은 클래스에서 @Autowired 코드를 적용해도 아무 기능도 동작하지 않는다.
'Spring > 핵심 원리 구현' 카테고리의 다른 글
생성자 주입을 선택하라 (1) | 2024.02.05 |
---|---|
옵션 처리 (0) | 2024.02.04 |
중복 등록과 충돌 (0) | 2024.02.02 |
필터 (0) | 2024.02.01 |
컴포넌트 스캔 기본 대상 (1) | 2024.01.31 |