ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 의존관계 자동 주입
    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;
    }

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

     

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

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

     

     

    'Spring > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글

    빈 스코프  (0) 2022.08.07
    빈 생명주기 콜백  (0) 2022.08.06
    컴포넌트 스캔  (0) 2022.08.05
    싱글톤 컨테이너  (0) 2022.08.04
    스프링 컨테이너와 스프링 빈  (0) 2022.08.04
Designed by Tistory.