Spring/스프링 핵심 원리 - 기본편

의존관계 자동 주입

한 면만 쓴 종이 2022. 8. 6. 01:05

[인프런] 스프링 핵심 원리 - 기본편

 

📌 의존관계 주입 방법

  1. 생성자 주입
  2. 수정자 주입 (setter 주입)
  3. 필드 주입
  4. 일반 메서드 주입

 

📍생성자 주입

 

지금까지 실습을 통해 했던 방식이 생성자 주입 방식이었음

📎생성자 호출 시점에 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;
}

📎최대한 스프링에서 제공해주는 기능을 사용하고, 뚜렷한 목적이 있을 시에 사용하는 것이 좋음

 

📍자동, 수동의 올바른 실무 운영 기준

  • 편리한 자동 기능을 기본으로 사용
  • 직접 등록하는 기술 지원 객체는 수동 등록 권장
  • 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고려해보는 것을 권장