-
의존관계 자동 주입Spring/스프링 핵심 원리 - 기본편 2022. 8. 6. 01:05
[인프런] 스프링 핵심 원리 - 기본편
📌 의존관계 주입 방법
- 생성자 주입
- 수정자 주입 (setter 주입)
- 필드 주입
- 일반 메서드 주입
📍생성자 주입
지금까지 실습을 통해 했던 방식이 생성자 주입 방식이었음
📎생성자 호출 시점에 1번만 호출되는 것을 보장
📎불변, 필수 의존관계에 사용
📎생성자가 1개만 있으면 @Autowired를 생략해도 자동 주입됨
@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 생략해도 자동 주입됨!
📍 수정자 주입(setter 주입)
setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계 주입
📎선택, 변경 가능성이 있는 의존관계에 사용
📎@Autowired는 주입할 대상이 없으면 오류 발생함 => @Autowired(required = false) 로 하면 오류 발생X
@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; } }
📍 필드 주입
📎일반적으로 잘 사용하지 않음
∵ 외부에서 변경 불가능하기 때문
@Component public class OrderServiceImpl implements OrderService { @Autowired private MemberRepository memberRepository; @Autowired private DiscountPolicy 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; } }
⭐순수한 자바 코드에서는 @Autowired가 당연히 작동하지 않음. @SpringBootTest처럼 스프링 컨테이너를 테스트에 포함한 경우에만 가능
📍 옵션 처리
주입할 스프링 빈이 없어도 동작해야 할 때 사용
📎@Autowired 는 기본값이 @Autowired(required=true) 임
↪ @Autowired(required=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출이 안됨
📎org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력됨
📎Optional<> : 자동 주입할 대상이 없으면 Optional.empty 가 입력됨
📍 생성자 주입 권장 이유
📎필드에 final 키워드 사용 가능 => 값 설정이 안 되어있으면 오류를 컴파일 시점에 막아줌
private final MemberRepository memberRepository; private final DiscountPolicy discountPolicy; @Autowired public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; } ```
📍 롬복과 최신 트랜드
📎@RequiredArgsConstructor 을 사용하면 final이 붙은 필드를 모아서 생성자를 자동 생성해줌
@Autowired는 생성자가 하나일 때 생략 가능하므로 아래와 같이 간결해짐
@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; } }
@Component @RequiredArgsConstructor public class OrderServiceImpl implements OrderService { private final MemberRepository memberRepository; private final DiscountPolicy discountPolicy; }
📍 조회 빈이 2개 이상 - 문제
📎@Autowired는 타입으로 조회함
@Autowired private DiscountPolicy discountPolicy
📎따라서 아래 코드와 유사하게 동작함
ac.getBean(DiscountPolicy.class)
⭐하지만 타입으로 조회하면 선택된 빈이 2개일 때 문제가 발생함
// FixDiscountPolicy와 RateDiscountPolicy 둘 다 스프링 빈으로 등록 @Component public class FixDiscountPolicy implements DiscountPolicy {} @Component public class RateDiscountPolicy implements DisocuntPolicy {} // 의존관계 자동 주입 @Autowired private DiscountPolicy discountPolicy /* 오류 발생 */
📍 조회 빈이 2개 이상 - 해결법
📎@Autowired 매칭
@Autowired는 타입 매칭을 시도만약 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭함
@Autowired public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy rateDiscountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = rateDiscountPolicy; }
📎@Qualifier 사용
@Qualifier 는 추가 구분자를 붙여주는 방법임
@Component @Qualifier("mainDiscountPolicy") public class RateDiscountPolicy implements DiscountPolicy {} ``` @Component @Qualifier("fixDiscountPolicy") public class FixDiscountPolicy implements DiscountPolicy {}
@Autowired public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; }
만약 @Qualifier("mainnDiscountPolicy를 못 찾으면 mainDiscountPolicy라는 이름의 스프링 빈을 추가로 찾음
=> @Qualifier 끼리 매칭 -> 빈 이름 매칭
하지만, @Qualifier를 찾는 용도로만 사용하는 걸 권장
📎@Primary 사용
@Autowired 시에 여러 빈이 매칭되면 @Primary가 우선권을 가짐
@Component @Primary public class RateDiscountPolicy implements DiscountPolicy {} @Component public class FixDiscountPolicy implements DiscountPolicy {}
⭐ @Primary, @Qualifier 활용
자주 사용하는 메인 데이터베이스의 커넥션을 획득하는 스프링 빈과 가끔 사용하는 서브 데이터베이스 커넥션을 획득하는 스프링 빈이 있을 때,
메인 데이터 스프링 빈 : @Primary
서브 데이터 스프링 빈 : @Qualifier 로 명시적으로 획득
의 방법 권장
📍 애노테이션 직접 만들기
/* 만든 애노테이션 */ 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) @Inherited @Documented @Qualifier("mainDiscountPolicy") public @interface MainDiscountPolicy { }
/* 만든 애노테이션 사용 */ @Component @MainDiscountPolicy //@RequiredArgsConstructor public class OrderServiceImpl implements OrderService {
@Autowired public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; }
📎최대한 스프링에서 제공해주는 기능을 사용하고, 뚜렷한 목적이 있을 시에 사용하는 것이 좋음
📍자동, 수동의 올바른 실무 운영 기준
- 편리한 자동 기능을 기본으로 사용
- 직접 등록하는 기술 지원 객체는 수동 등록 권장
- 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고려해보는 것을 권장
'Spring > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
빈 스코프 (0) 2022.08.07 빈 생명주기 콜백 (0) 2022.08.06 컴포넌트 스캔 (0) 2022.08.05 싱글톤 컨테이너 (0) 2022.08.04 스프링 컨테이너와 스프링 빈 (0) 2022.08.04