생성자를 통해 의존 관계를 주입 받는 방법
필드 값을 변경하는 수정자 메서드 setter를 통해 의존관계를 주입하는 방법
setter 생성
@Autowired
public void setMemberRepository(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
⭐️ 만약 필드가 필수가 아니면, @Autowired(required = false)로 주면 됨
주입할 대상이 없으면, 오류가 발생하니 주의
자바는 과거부터 필드 값을 직접 변경하지 않고, set00, get00라는 메서드를 통해 값을 수정하고, 읽는 규칙이 있음
필드에 바로 주입하는 방법
@Autowired private MemberRepository memberRepository;
아무 메서드에 @Autowired 사용
주입할 스프링빈이 없어도 (optional) 동작해야할 때
@Autowired의 주입 대상이 없으면, 무조건 오류가 발생
@Autowired(required=false): 자동 주입 대상이 없으면 setter 호출 불가 @Autowired(required = false)
org.springframework.lnag.@Nullable: 자동 주입 대상이 없으면 null 입력public void setNoBean2(@Nullable Member noBean2) {
Optional<> 자동 주입할 대상이 없으면 Optional.empty 입력public void setNoBean3(Optional<Member> noBean3) {
여러 의존 관계 주입 방식이 있지만, 그 중 생성자 주입을 선택!!
생성자 주입을 편하게 해주는 라이브러리
대부분 불변이므로 final 키워드를 사용함
프로젝트 생성할 때 롬복을 체크하고 할 수도 있음, build.gradle에 추가하기
설정 > 컴파일러 > 어노테이션 프로세서 > 활성화 클릭
@Getter, @Setter, @ToString
어노테이션만 추가하면, 해당 메서드를 자동으로 만들어줌
⭐️ @RequiredArgsConstructor: final 키워드가 붙은 필드의 생성자를 자동으로 만들어줌
컴파일 시점에 롬복이 생성자 코드를 생성
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
// 생성자 없어도 됨
}
최근 스타일
생성자 1개 (Autowired 생략) + @RequiredArgsConstructor 조합으로 사용
@Autowired는 타입으로 조회함
같은 타입인 빈이 2개 이상일 때는 NoUniqueBeanDefinitionException 발생
하위 타입을 지정하면 DIP를 위반함.
1. @Autowired 필드명 매칭
@Autowired 매칭
@Autowired
private DiscountPolicy discountPolicy
//===== 변경한 코드
private DiscountPolicy rateDiscountPolicy
2. @Qulifier 사용
추가 구분자, 빈 이름 변경하는 것 X
@Qualifier는 @Qualifier를 찾는 용도로만 사용하는 것이 좋다
@Component
@Qualifier("mainDiscountPolicy")
private DiscountPolicy discountPolicy
3. @Primary 사용
우선 순위 지정, @Autowired로 여러 빈이 매칭되면 @Primary가 우선권 가짐
메인 DB를 주로 Primary로 둠
@Component
@Primary
private DiscountPolicy discountPolicy
우선 순위
@Primary vs @Qualifier
Qualifier가 Primary보다 상세하게 동작하고, 우선권이 있음
@Qualifier("mainDiscountPolicy") 이런식으로 문자를 적는 경우 컴파일 시
타입 체크가 안 됨 → 애노테이션을 직접 만들어 해결
core/annotation 패키지 생성
아래 annotation 타입으로 체크하고 MainDiscountPolicy 생성
package hello.core.annotation;
import org.springframework.beans.factory.annotation.Qualifier;
import java.lang.annotation.*;
@Target({
ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.TYPE,
ElementType.ANNOTATION_TYPE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {}
이렇게 생성해주고, main으로 사용할 할인 정책에서는
Qualifier("mainDiscountPolicy") 이렇게 쓰지 않고, 만든 애노테이션
@MainDiscountPolicy를 적어줌
@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {
...
대부분은 스프링이 제공하는 애노테이션에서 해결이 됨.
뚜렷한 목적 없이 무분별한 재정의는 유지보수에 혼란을 가중할 수 있으니 주의
Map<String, DiscountPolicy>
key = 빈이름, value = 값으로 DiscountPolicy 타입으로 조회한 모든 스프링 빈 담아줌
List<DiscountPolicy> :
DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아줌.
만약 해당하는 타입의 스프링 빈이 없으면, 빈 컬렉션이나 Map을 주입
결론) 자동 스캔을 선호하는 추세, 자동 빈 등록을 해도 OCP, DIP를 지킬 수 있음
업무 로직 빈은 자동 등록, 기술 지원 빈은 수동 등록을 사용하는 것이 좋음.
어플리케이션은 업무와 기술지원 로직으로 나눌 수 있음
추가로 비즈니스 로직에서 다형성을 적극 활용할 때
다른 개발자가 코드를 파악하기 위해서 여러 코드를 열어봐야하므로, 수동 등록을 하거나, 자동 등록시 같은 패키지에 묶어두는 것이 좋다.
예를 들어)
DiscountService의 경우 DiscountPolicy가 여러 개이기 때문에,
한 눈에 알아볼 수 있도록 별도의 설정 정보를 생성하여 수동 등록하는 것이 좋다.
@Configuration
public class DiscountPolicyConfig{
@Bean
public DiscountPolicy rateDiscountPolicy(){
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy(){
return new FixDiscountPolicy();
}
}