반응형
의존관계 주입
- 생성자 주입
- 수정자 주입(setter 주입)
- 필드 주입
- 일반 메서드 주입
생성자 주입
- 생성자를 통해서 의존 관계를 주입받는 방법
- 생성자 호출시점에 딱 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;
}
}
수정자(setter) 주입
- setter라 불리는 필드 값을 변경하는 수정자 메서드를 통해 의존 관계를 주입하는 방식
- 선택, 변경 가능성이 있는 의존관계에 사용
- 예시
@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;
}
}
필드 주입
- 필드에 바로 주입하는 방식
- 코드가 간결하나 외부에서 변경이 불가능해서 테스트가 힘들다.
- 또한 DI 프레임워크 없이는 동작할 수 없다.
- 예시
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
일반 메서드 주입
- 일반 메서드를 통해 의존 관계를 주입받는다.
- 한 번에 여러 필드를 주입받을 수 있다.
- 일반적으로 잘 사용하지 않는다.
옵션 처리
- @Autowired의 기본 옵션은 required = true이다.
- required 옵션이 true이면 자동 주입 대상이 없을 때 오류가 발생한다.
- 자동 주입 대상을 옵션으로 처리하는 방법은 다음과 같다.
- @Autowired(required = false) : 자동 주입할 대상이 없으면 메서드 자체가 호출되지 않는다.
- org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력
- Optional<> : 자동 주입할 대상이 없으면 Optional.empty가 입력
- 예시
//호출 안됨
@Autowired(required = false)
//Member 클래스는 스프링 빈으로 관리되지 않는다.
public void setNoBean1(Member member) {
System.out.println("setNoBean1 = " + member);
}
//null 호출
@Autowired
public void setNoBean2(@Nullable Member member) {
System.out.println("setNoBean2 = " + member);
}
//Optional.empty 호출
@Autowired
public void setNoBean3(Optional<Member> member) {
System.out.println("setNoBean3 = " + member);
}
생성자 주입
- 최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장
- 대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료 시점까지
의존 관계가 변경되지 않는 것이 대부분 - 수정자 주입을 사용하는 경우 set 메서드를 public으로 열어둬야 하기 때문에
누군가 실수로 값을 변경하는 등의 문제가 발생할 수 있다.
final 키워드
- 생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다.
- final 키워드를 사용하면 오로지 생성자에서만 초기화가 가능하다.
- 때문에 생성자에서 필드 값을 초기화하지 않는다면 컴파일 오류가 발생한다.
롬복
- 생성자 주입의 초기 코드 예시를 보자
@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를 생략할 수 있다.
- 따라서 다음과 같이 @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;
}
}
- 이 상태에서 롬복을 적용해보자.
- 롬복 라이브러리가 제공하는 @RequiredArgsConstructor 기능을 사용하면,
final이 붙은 필드를 모아서 생성자를 자동으로 만들어준다. - 이 기능을 사용하면 코드에는 보이지 않지만 실제로는 호출이 가능하다.
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
}
- 롬복을 사용하면 위와 같이 코드를 단순화할 수 있다.
롬복 라이브러리 적용 방법
- build.gradle에 라이브러리 및 환경 추가
//lombok 설정 추가 시작
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
//lombok 설정 추가 끝
dependencies {
//lombok 라이브러리 추가 시작
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
//lombok 라이브러리 추가 끝
- (윈도우) File -> Settings -> plugin -> lombok 검색 설치 실행
조회되는 빈이 2개 이상일 때
- @Autowired는 타입으로 조회
- 마치 .getBean(부모클래스.class)처럼 동작
- 부모 클래스의 하위 타입이 여러 개일 경우 하위 타입을 모두 스프링 빈으로 등록하고
의존관계 자동 주입을 실행하면 NoUniqueBeanDefinitionException 오류가 발생 - 이때 하위 타입으로 의존 관계 주입을 실행하면 오류는 해결되지만,
하위 타입으로 지정하는 것은 DIP를 위배하고 유연성이 떨어진다. - 또한 이름만 다르고, 완전히 똑같은 타입의 스프링 빈이 2개 있을 경우
하위 타입으로 지정하는 방식은 문제를 해결할 수 없다.
해결 방법
- @Autowired 필드 명 매칭
- @Qualifier -> @Qualifier끼리 매칭 -> 빈 이름 매칭
- @Primary 사용
@Autowired 필드 명 매칭
- @Autowired는 타입 매칭을 시도하고, 빈이 여러 개면 필드 이름, 파라미터 이름으로
빈 이름을 추가 매칭 한다. - 기존 코드
@Autowired
private DiscountPolicy discountPolicy
- 필드 명을 빈 이름으로 변경
@Autowired
private DiscountPolicy rateDiscountPolicy
- 필드 명을 연결하려고 하는 하위 타입의 이름으로 변경
- 필드 명 매칭은 먼저 타입 매칭을 시도하고 그 결과에 여러 빈이 있을 때 추가로 동작하는 기능이다.
@Qualifier 사용
- @Qualifier는 추가 구분자를 붙여주는 방식
- 주입 시 추가적인 방법을 제공하는 것으로 빈 이름을 변경하는 것은 아니다.
- 예시
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
- 빈 등록 시 @Qualifier를 붙인다.
- 주입 시에 @Qualifier를 붙여주고 등록한 이름을 적어준다.
- 예시
//생성자 자동 주입
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
//수정자 자동 주입
@Autowired
public DiscountPolicy setDiscountPolicy(
@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
return discountPolicy;
}
- @Qualifier 사용 시 지정한 @Qualifier의 이름을 찾지 못하면,
지정한 이름으로 등록된 스프링 빈이 있는지 추가적으로 탐색한다.
@Primary 사용
- @Primary는 우선순위를 정하는 방법이다.
- @Autowired 시에 여러 빈이 매칭 되면 @Primary가 우선권을 갖는다.
- 예시 : rateDiscountPolicy에게 우선권을 부여하기
@Component
@Primary //우선권 부여
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
- RateDiscountPolicy에 우선권이 부여되어 있다.
- @Autowired를 사용해 DiscountPolicy에 의존 관계 부여 시 RateDiscountPolicy가 우선권을 갖는다.
@Primary, @Qualifier 사용
- @Primary와 @Qualifier가 동시에 사용될 때 @Qualifier의 우선순위가 높다.
- 메인 데이터베이스의 커넥션을 획득하는 스프링 빈은 @Primary를 사용하는 것이 좋다.
- 서브 데이터베이스의 커넥션 빈을 획득할 때는 @Qualifier를 사용하는 것이 좋다.
수동 빈 등록 vs 자동 빈 등록
- 애플리케이션은 크게 업무 로직과 기술 지원 로직으로 나눌 수 있다.
- 업무 로직 빈
웹을 지원하는 컨트롤러, 핵심 비즈니스 로직이 있는 서비스,
데이터 계층의 로직을 처리하는 리포지토리 등이 모두 업무 로직이다.
보통 비즈니스 요구사항을 개발할 때 추가되거나 변경된다. - 기술 지원 빈
기술적인 문제나 공통 관심사(AOP)를 처리할 때 주로 사용된다.
데이터베이스 연결이나, 공통 로그 처리처럼 업무 로직을 지원하기 위한
하부 기술이나 공통 기술들을 말한다.
- 업무 로직 빈
- 업무 로직은 로직의 수도 많고 개발할 때 유사한 패턴을 보이는 경우가 많기 때문에
자동 빈 등록 기능을 적극적으로 활용한다. - 기술 지원 로직은 업무 로직에 비해 그 수가 현저히 적고, 애플리케이션 전반에 걸쳐
광범위하게 영향을 미친다. 또 업무 로직은 문제가 발생했을 때 어디가 문제인지
명확하게 잘 드러나지만, 기술 지원 로직은 문제 파악이 비교적 힘들다.
따라서 이러한 기술 지원 로직들은 가급적 수동 빈 등록을 사용한다. - 업무 로직 중에서도 다형성을 적극 활용할 때는 수동 빈 등록을 사용한다.
예를 들어, 할인 정책이 여러 개이고 그때그때 의존 관계를 다르게 주입해야 할 때,
자동 빈 등록을 사용하면 어떤 빈이 주입되는지, 빈의 이름이 무엇인지 한눈에 파악하기 힘들다.
이럴 때는 별도의 설정 정보를 만들어 한눈에 파악하기 좋게 만든다.
자동 빈 등록을 사용하는 경우에는 해당 빈들을 특정 패키지에 같이 묶어 한눈에 파악하기 좋게 만든다.
출처 : 인프런 김영한 님 스프링 강의
반응형
'내가 공부하려고 올리는 > 스프링' 카테고리의 다른 글
빈 스코프 (0) | 2022.02.06 |
---|---|
빈 생명 주기 콜백 (0) | 2022.02.04 |
컴포넌트 스캔 (0) | 2022.02.03 |
싱글톤 컨테이너 (0) | 2022.02.02 |
스프링 컨테이너와 스프링 빈 (0) | 2022.02.02 |
댓글