[SPRING] 스프링 핵심 원리 이해 - 의존관계 자동 주입
김영한 강사님의 스프링 핵심 원리 - 기본편 강의를 정리한 내용입니다.
다양한 의존관계 주입 방법
✔ 생성자 주입
- 생성자를 통해 의존관계를 주입받는 방법이다.
- 생성자 호출 시점에 딱 1번만 호출된다.
- 불변하고 필수적인 의존관계에 사용한다.
@Autowired // 생성자 1개면 생략 가능!
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
🚨 생성자가 한개라면 @Autowired를 생략해도 자동 주입된다 🚨
✔ 수정자 주입(setter 주입)
- 변경 가능성이 있는 의존관계에 사용한다.
✔ 필드 주입
- 코드가 간결하지만 외부에서 변경이 불가능해 테스트를 하기 힘들다.
- DI 프레임워크가 없으면 아무것도 할 수 없다.
✔ 일반 메소드 주입
- 한번에 여러 필드를 주입 받을 수 있다.
옵션 처리
- @Autowired을 사용하면 required 기본값이 true이기 때문에 자동 주입 대상이 없으면 오류가 난다.
- required = false : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안된다.
- @Nullable : 자동 주입할 대상이 없으면 null이 입력된다.
- Optional<> : 자동 주입할 대상이 없으면 Optional.empty 가 입력된다.
생성자 주입을 쓰는 이유
- 객체를 생성할 때 1번만 호출되기 때문에 불변하게 설계할 수 있다.
- 생성자 주입을 사용하면 데이터를 누락했을 때 컴파일 오류가 발생한다.
- final 키워드를 사용할 수 있기 때문에 생성자에 값이 설정되지 않았다면 컴파일 오류로 알 수 있다.
롬복(Lombok)
롬복 라이브러리가 제공하는 @RequiredArgsConstructor 기능을 사용하면 final이 붙은 필드들로 생성자를 자동으로 만들어준다.
롬복 적용
- build.gradle에 아래 코드를 추가한다.
//lombok 설정 추가
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
//dependencies 안에 lombok 라이브러리 추가
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
- Preferences -> Annotation Processors -> Enable annotation processing 체크
@Autowired 필드 명, @Qualifier, @Primary
🚨 만약 조회된 빈이 두개 이상이라면?
👉 @Autowired 필드 명, @Qualifier, @Primary 사용
@Autowired 필드 명 매칭
@Autowired 는 타입 매칭을 시도하고 만약 여러개의 빈이 있으면 필드 이름 혹은 파라미터 이름으로 빈 이름을 추가 매칭한다.
@Autowired
private DiscountPolicy rateDiscountPolicy
@Qualifier 사용
@Qualifier는 추가 구분자를 붙여주는 방법으로 빈의 이름 자체를 변경하는 건 아니다.
// 빈 등록 시 @Qualifier를 붙인다.
@Qualifier("mainDiscountPolicy")
// 주입 시에 @Qualifier를 붙여주고 등록한 이름을 적어준다.
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
- @Qualifier("mainDiscountPolicy")를 못찾으면 mainDiscountPolicy 이름을 가진 스프링빈을 조회한다.
- 컴포넌트 외에 빈 등록 시에도 @Qualifier를 동일하게 사용할 수 있다.
@Primary 사용
@Primary는 우선순위를 정하는 방법이다. @Autowired 시에 여러 빈이 매칭되면 Primary가 우선권을 가진다.
📌 @Primary와 @Qualifier 둘 중 @Qualifier가 우선순위가 더 높다.
어노테이션 직접 만들기
@Qualifier("mainDiscountPolicy")를 쓰면 소괄호에서 글자를 틀려도 컴파일러에서 체크해주지 않아 에러가 발생할 수 있으므로 어노테이션을 직접 만들어 쓰기도 한다.
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
------------------------------------------------------------
@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {}
------------------------------------------------------------
//생성자 자동 주입
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
List, Map
조회한 빈이 모두 필요할 경우
@Test
void findAllBean(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
DiscountService discountService = ac.getBean(DiscountService.class);
Member member = new Member(1L, "userA", Grade.VIP);
int discountPrice = discountService.discount(member, 10000, "fixDiscountPolicy")
}
static class DiscountService{
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
@Autowired
public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
}
public int discount(Member member, int price, String discountCode) {
DiscountPolicy discountPolicy = policyMap.get(discountCode);
return discountPolicy.discount(member, price);
}
}
로직 분석
- DiscountService는 스프링 컨테이너에 등록되면서 Map으로 모든 DiscountPolicy를 주입받는다.
- discount() 메소드는 discountCode로 "fixDiscountPolicy"가 넘어오면 map에서 fixDiscountPolicy 스프링 빈을 찾아서 실행한다.
주입 분석
- Map<String, DiscountPolicy> : map의 키에 스프링 빈의 이름을 넣어주고, 그 값으로
DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다. - List< DiscountPolicy> : DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
만약 해당하는 타입의 스프링 빈이 없으면, 빈 컬렉션이나 Map을 주입한다.
📌 스프링 컨테이너는 생성자에 클래스 정보를 받는다. 여기에 클래스 정보를 넘기면 해당 클래스가 스프링 빈으로 자동 등록된다.
new AnnotationConfigApplicationContext(AutoAppConfig.class,DiscountService.class);
💡 수동 빈 등록은 언제 사용하는게 좋을까?
🚩 어플리케이션에 전체적으로 영향을 미치는 기술 지원 객체(데이터 베이스 연결, 공통 로그 처리 등)는 수동으로 빈을 등록해서 설정 정보에 나타나게 하는게 유지보수에 좋다.
Author And Source
이 문제에 관하여([SPRING] 스프링 핵심 원리 이해 - 의존관계 자동 주입), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@3hee_11/SPRING-스프링-핵심-원리-이해-의존관계-자동-주입저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)