스프링 핵심 원리[기본편] - 의존관계 자동 주입

2022. 12. 28. 09:48·Spring

의존관계 주입 방법

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

생성자 주입

  • 생성자를 통해 의존관계를 주입받는 방법
  • 지금까지 진행한 방법

특징

  • 생성자 호출시점에 딱 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;
    }
}
  • 생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입된다.(물론 스프링 빈에만 해당)

수정자 주입(setter 주입)

  • setter를 통해 필드의 값을 변경하는 수정자 메서드를 통해 의존관계를 주입

특징

  • 선택 , 변경 가능성이 있는 의존관계에 사용
  • 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방식
더보기
참고: @Autowired 의 기본 동작은 주입할 대상이 없으면 오류가 발생한다. 주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정하면 된다.
@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;
}

일반 메서드 주입

  • 일반 메서드를 통해 주입받을수 있다.

특징

  • 한번에 여러필드를 주입받을수 있다.
  • 잘 사용안함
@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(required=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안됨
  • org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력된다.
  • Optional<> : 자동 주입할 대상이 없으면 Optional.empty 가 입력된다.

생성자 주입을 선택해라!

final 키워드

  • 생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다. 그래서 생성자에서 혹시라도 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.

불변

누락

조회 빈이 2개 이상 - 문제

  • @Autowired 는 타입(Type)으로 조회한다.
@Autowired
private DiscountPolicy discountPolicy
  • 다음과 같이 같은 타입(부모타입)을 갖고 있는 스프링 빈이 두개라면 NoUniqueBeanDefinitionException 오류가 발생
@Component
public class FixDiscountPolicy implements DiscountPolicy {}

@Component
public class RateDiscountPolicy implements DiscountPolicy {}
  • 하위 타입으로 지정할 수 도 있지만, 하위 타입으로 지정하는 것은 DIP를 위배하고 유연성이 떨어진다. 그리고 이름만 다르고, 완전히 똑같은 타입의 스프링 빈이 2개 있을 때 해결이 안된다.

해결방안

@Autowired 필드 명 매칭

  • @Autowired는 타입 매칭을 시도하고, 여러빈이 있으면 필드이름, 파라미터 이름으로 빈이름을 추가한다.
  • 스프링 컨테이너에서 DiscountPolicy 타입을 탐색하고 2개이상일시 rateDiscountPolicy를 이름으로 탐색
@Autowired
private DiscountPolicy rateDiscountPolicy

@Qualifier 사용

  • @Qualifier는 추가 구분자를 붙여주는 방법
  • 빈 등록시 @Qualifier를 붙여 준다(자동등록 , 수동등록 동일)
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
@Bean
@Qualifier("mainDiscountPolicy")
public DiscountPolicy discountPolicy() {
	return new ...
}
  • 주입시에 @Qualifier를 붙여주고 등록한 이름을 적어준다.
  • 만약 @Qualifier로 주입할때 @Qualifier("mainDiscountPolicy")를 못찾으면 mainDiscountPolicy라는 이름의 스프링빈을 추가로 찾는다. 하지만 @Qualifier는 @Qualifier를 찾을때만 사용하는것이 좋다.
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
    @Qualifier("mainDiscountPolicy") DiscountPolicy
    discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
}

@Primary 사용

  • 우선순위를 정하는 방법이다. 같은 타입의 스프링빈이 있다면 등록시에 @Prime이 있는 스프링빈이 우선 순위를 갖는다.
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}

우선권

  • @Qualifier > @Prime -> 항상 좀더 세세한 것에 우선권을 준다.
  • 조회한 빈이 모두 필요할 때, List, Map
  • 의도적으로 해당 타입의 스프링 빈이 모두 필요할수 있다.
  • ex ) 할인서비스를 제공하는데, 클라이언트가 할인의 종류를 선택할수 있다고 가정한다면 같은 타입 DiscountPolicy로 하는 모든 빈(rate, fix)이 필요하다. 
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);
    	}
    }
}

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

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

  • 설정 정보를 기반으로 애플리케이션을 구성하는 부분과 실제 동작하는 부분을 명확하게 나누는 것이 이상적이지만, 개발자 입장에서 스프링 빈을 하나 등록할 때 @Component 만 넣어주면 끝나는 일을 @Configuration 설정 정보에 가서 @Bean 을 적고, 객체를 생성하고, 주입할 대상을 일일이 적어주는 과정은 상당히 번거롭다.
  • 또 관리할 빈이 많아서 설정 정보가 커지면 설정 정보를 관리하는 것 자체가 부담이 된다. 그리고 결정적으로 자동 빈 등록을 사용해도 OCP, DIP를 지킬 수 있다.

수동 빈 등록은 언제 사용하면 좋을까?

  • 기술 지원 빈: 기술적인 문제나 공통 관심사(AOP)를 처리할 때 주로 사용된다. 데이터베이스 연결이나, 공통 로그 처리 처럼 업무 로직을 지원하기 위한 하부 기술이나 공통 기술들이다.
  • 애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체는 수동 빈으로 등록해서 딱! 설정 정보에 바로 나타나게 하는 것이 유지보수 하기 좋다.
  • 비즈니스 로직 중에서 다형성을 적극 활용할 때(DiscountPolicy 안에 rate와fix같이 여러개의 역할이 있는경우)

참고강의

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8

728x90
저작자표시 (새창열림)

'Spring' 카테고리의 다른 글

[spring] 게시판 CRUD 만들기  (0) 2022.12.30
JPA Auditing기능이란?  (0) 2022.12.29
스프링 핵심 원리[기본편] - 컴포넌트 스캔  (0) 2022.12.27
스프링 핵심 원리[기본편] - 싱글톤 컨테이너  (0) 2022.12.27
스프링 핵심 원리[기본편] - 스프링 컨테이너와 스프링 빈  (0) 2022.12.27
'Spring' 카테고리의 다른 글
  • [spring] 게시판 CRUD 만들기
  • JPA Auditing기능이란?
  • 스프링 핵심 원리[기본편] - 컴포넌트 스캔
  • 스프링 핵심 원리[기본편] - 싱글톤 컨테이너
study ticket
study ticket
  • study ticket
    혼자하는 공부
    study ticket
  • 전체
    오늘
    어제
    • 개발 (77)
      • 오류 (1)
      • Spring (13)
      • Java (0)
      • Data structure (6)
      • Algorithm (49)
        • 백준 (17)
        • 프로그래머스 (2)
      • 문제풀면서 알게되는것들 끄적 (2)
      • 머신러닝 (4)
        • sklearn (3)
        • pandas (1)
      • 프로젝트 (0)
        • 핏두 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백준1157
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
study ticket
스프링 핵심 원리[기본편] - 의존관계 자동 주입
상단으로

티스토리툴바