07. Spring Basic - 의존관계 자동 주입 (2)

MoonJaeGyeong·2023년 9월 21일

Spring

목록 보기
7/10
post-thumbnail

1. 조회 빈이 2개 이상


@Autowired 는 타입으로 조회한다.

@Autowired
private DiscountPolicy discountPolicy

이는 ac.getBean(DiscountPolicy.class) 와 유사하게 작동한다.

스프링 빈 조회에서 했듯이 타입으로 조회 시 선택된 빈이 2개 이상일 때 문제가 발생한다.

@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@Component
public class RateDiscountPolicy implements DiscountPolicy {}

그리고 아래처럼 의존관계 자동 주입을 하면

@Autowired
private DiscountPolicy discountPolicy

NoUniqueBeanDefinitionException 오류가 발생한다.
이는 스프링 빈을 수동 등록해서 해결해도 되지만, 의존 관계 자동 주입에서 해결하는 여러 방법이 있다.

1.1 @Autowired 필드 명 매칭


**기존코드** ```java @Autowired private DiscountPolicy discountPolicy ```

필드 명을 빈 이름으로 변경

@Autowired
private DisocountPolicy rateDiscountPolicy

필드 명이 rateDiscountPolicy 이므로 정상 주입된다.
필드 명 매칭은 먼저 타입 매칭을 시도 하고 그 결과에 여러 빈이 있을 때 추가로 동작하는 기능이다.

@Autowired 정리

  1. 타입 매칭
  2. 타입 매칭의 결과가 2개 이상일 때 필드 명, 파라미터 명으로 빈 이름 매칭

1.2 @Qualifier 사용


`@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") DiscountPolicydiscountPolicy) {
 	this.memberRepository = memberRepository;
 	this.discountPolicy = discountPolicy;
}

수정자 자동 주입

@Autowired
public DiscountPolicy setDiscountPolicy(@Qualifier("mainDiscountPolicy") 
DiscountPolicy discountPolicy) {
 	this.discountPolicy = discountPolicy;
}

@Qualifier 정리

  1. @Qualifier끼리 매칭
  2. 빈 이름 매칭
  3. NoSuchBeanDefinitionException 예외 발생

1.3 @Primary


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

rateDisocuntPolicy 가 우선권을 갖도록 세팅

@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}

@Component
public class FixDiscountPolicy implements DiscountPolicy {}

이와 같이 한 후 코드를 실행해보면 아무 문제 없이 @Primary 가 잘 동작하는 것을 확인할 수 있다.

1.4 @Primary, @Qualifier 비교


@Primary, @Qualifier 활용

코드에서 자주 사용하는 메인 데이터베이스의 커넥션을 획득한 스프링 빈, 특별한 기능으로 가끔 사용하는 서브 데이터베이스의 커넥션을 획득하는 스프링 빈이 있다고 할 때

메인 데이터베이스 : @Primary
서브 데이터베이스 : @Qualifier 를 사용하면 코드를 깔끔하게 관리할 수 있다.

우선순위

@Primary는 기본값 처럼 동작하는 것이고, @Qualifier 는 매우 상세하게 동작하기 때문에
@Qualifier 의 우선 순위가 높다.

1.5 애노테이션


@Qualifier("mainDiscountPolicy) 이렇게 문자를 적으면 컴파일시 타입 체크가 안되는데 이는 애노테이션을 만들어서 문제를 해결할 수 있다.

package hello.core.annotataion;

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)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {}


@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {}
//생성자 자동 주입
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
 @MainDiscountPolicy DiscountPolicy discountPolicy) {
	 this.memberRepository = memberRepository;
 	this.discountPolicy = discountPolicy;
}
//수정자 자동 주입
@Autowired
public DiscountPolicy setDiscountPolicy(@MainDiscountPolicy DiscountPolicy
discountPolicy) {
	 this.discountPolicy = discountPolicy;
}

2. List, Map 조회와 빈 모두 동시에


웹서비스에서는 많은 경우가 존재한다. 같은 할인이더라도 다른 쿠폰으로 다르게 적용 되어서 (fix,rate) 인 두 개의 할인 정책이 모두 필요할 수 있다.

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.junit.jupiter.api.Test;
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.assertThat;

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);
     }
     
     static class DiscountService {
     
       private final Map<String, DiscountPolicy> policyMap;
       private final List<DiscountPolicy> policies;
       
       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);
       
       System.out.println("discountCode = " + discountCode);
       System.out.println("discountPolicy = " + discountPolicy);
       
       return discountPolicy.discount(member, price);
 		}
 	}
 }

로직 분석

  • DiscountService 는 Map으로 모든 DiscountPolicy를 주입받는다. 이떄 fixDiscountPolicy,
    rateDiscountPolicy가 주입된다.
  • discount () 메서드는 discountCode로 "fixDiscountPolicy" 가 넘어오면 map 에서 fixDiscountPolicy 스프링 빈을 찾아서 실행한다.

주입 분석

  • Map<String, DiscountPolicy> : map의 키에 스프링 빈의 이름을 넣어주고, 그 값으로 DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
  • List<DiscountPolicy : DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
  • 만약 해당하는 타입의 스프링 빈이 없으면, 빈 컬렉션이나 Map 을 주입한다.

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


편리한 자동 기능을 기본으로 사용

스프링은 @Component 분만 아니라 @Controller, @Service, @Repository 처럼 계층에 맞추어 일반적인 애플리케이션 로직을 자동으로 스캔할 수 있도록 지원한다.
관리할 빈이 많아지면 관리도 부담이 되고 자동 빈 등록을 사용해도 OCP, DIP를 지킬 수 있다.

수동 빈 등록 사용 시기

  • 업무 로직 빈: 웹을 지원하는 컨트롤러, 핵심 비즈니스 로직이 있는 서비스, 데이터 계층의 로직을 처리하는 리포지토리등이 있다. (자동 빈 등록)
  • 기술 지원 빈: 기술적인 문제나 공통 관심사(AOP) 를 처리할 떄 주로 사용. 데이터베이스 연결이나 공동 로그처리 같은 공통 기술들이다. (수동 빈 등록)

정리

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

<출처 : 스프링 핵심 원리 - 기본편 by 김영한>

profile
내 맘대로 끄적이는 개발 블로그

0개의 댓글