개발놀이터

스프링 @Autowired, @Component, @Qualifier, @Primary 본문

Spring/Spring

스프링 @Autowired, @Component, @Qualifier, @Primary

마늘냄새폴폴 2021. 8. 15. 00:25

*@Autowired 
스프링 빈의 의존관계를 자동으로 주입해주는 어노테이션이다. @Component 어노테이션으로 컴포넌트 스캔으로 등록된 클래스에서만 사용이 가능 하다. 컴포넌트 스캔으로 
등록되지 않은 클래스에서 Autowired만 선언하면 아무런 의미가 없다. 기존의 @Bean을 이용하여 스프링빈에 등록하고 의존관계를 주입하는 것보다 훨씬 편한 방법으로 
의존관계를 주입할 수 있다. 객체지향언어가 가져야할 원리다섯가지 SOLID의 D에 해당하는 DIP에 부합하는 어노테이션이다. 


*@Component 
컴포넌트 스캔을 실행할 때 스캔 대상이다. 스프링은 @ComponentScan 어노테이션을 보면 @Component 가 붙어있는 클래스들을 쭉 돌아서 찾아내고 스프링빈으로 등록한다.
스프링 빈에 등록된 빈들은 싱글톤 패턴을 적용하여 하나만 생성한 후 호출이 될때마다 생성된 빈을 가져다 쓰는 식으로 돌아간다. 


*@Autowired로 지정된 의존관계 주입(DI)에서 같은 프로퍼티인 빈이 여러개 존재할 때
조회 대상 빈이 2개 이상일 때 해결방법은 다음과 같다. 
1. @Autowired 필드 명 매칭
2. @Qualifier -> @Qualifier끼리 매칭 -> 빈 이름 매칭
3. @Primary 사용

1. @Autowired 필드 명 매칭
@Autowired는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
필드명이 정상 주입되는 것을 찾아서 자동으로 매칭시켜준다.

2. @Qualifier 사용
@Qualifier는 추가 구분자를 붙여주는 방법이다. 주입 시 추가적인 방법을 제공하는 것이지 빈 이름을 변경하는 것은 아니다.
어노테이션을 추가로 붙이는데 @Qualifier("mainDiscountPolicy")를 적고 생성자에서 

@Autowired
    public OrderServiceImpl(MemberRepository memberRepository,@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }


이렇게 추가해주면 된다.

3. @Primary 사용
@Primary는 우선순위를 정하는 방법이다. @Autowired시에 여러 빈이 매칭되면 @Primary가 우선권을 가진다.


*조회한 빈이 모두 필요할 때, LIst, Map
의도적으로 해당 타입의 스프링 빈이 다 필요한 경우도 있다.
예를 들어서 할인 서비스를 제공하는데, 클라이언트가 할인 종류를 선택할 수 있다고 가정해보자, 스프링을 사용하면 소위 말하는 전략패턴을 매우 간단하게 구현할 수 있다.

 

package hello.core.autowired;

import hello.core.AutoAppConfig;
import hello.core.discount.DiscountPolicy;
import hello.core.member.Grade;
import hello.core.member.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.*;

public class AllBeanTest {

    @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");

        assertThat(discountService).isInstanceOf(DiscountService.class);
        assertThat(discountPrice).isEqualTo(1000);

        int rateDiscountPrice = discountService.discount(member, 20000, "rateDiscountPolicy");
        assertThat(rateDiscountPrice).isEqualTo(2000);
    }

    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;
            System.out.println("policyMap = " + policyMap);
            System.out.println("policies = " + policies);
        }

        public int discount(Member member, int price, String discountCode) {
            DiscountPolicy discountPolicy = policyMap.get(discountCode);
            return discountPolicy.discount(member, price);
        }
    }
}

'Spring > Spring' 카테고리의 다른 글

스프링 빈 스코프  (0) 2021.08.16
스프링 빈 생명주기 콜백  (0) 2021.08.16
스프링빈 조회  (0) 2021.08.13
DI와 스프링빈  (0) 2021.08.13
Spring 테스트클래스  (0) 2021.08.10