본문 바로가기
Back-End/Spring

7.의존관계 자동주입

by 두두리안 2021. 1. 11.
728x90

7.의존관계 자동주입

  • 목차
    • 1.다양한 읜존관계 주입 방법
    • 2.옵션 처리
    • 3.생성자 주입을 선택하라
    • 4.롬복과 최신트렌드
    • 5.@Autowired 필드명 , @Qualifier , @Primary
    • 6.자동,수동의 올바른 실무운영기준

1.다양한 읜존관계 주입 방법

  • 생성자 주입
  • 수정자 주입 (setter)
  • 필드 주입
  • 일반 메서드 주입

OrderServiceImpl

@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
    //private final DiscountPolicy discountPolicy = new FixDiscountPolicy();

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

}
  • 생성자 호출 시점에서 딱1번 호출되는것을 보장
  • 불변,필수 의존관계에 사용
  • 생성자가 1개일 경우 @Autowired 를 생략해도 자동으로 주입된다

2.수정자 주입 (setter)

@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;
    }
}
  • 선택,변경 가능성있는 의존관계 사용

3.필드 주입

@Component
public class OrderServiceImpl implements OrderService {
 @Autowired
 private MemberRepository memberRepository;
 @Autowired
 private DiscountPolicy discountPolicy;
}
  • 외부에서 변경이 불가능해서 테스트하기 힘들다
  • DI 프레임워크가 없으면 안된다
  • 테스트 코드 에서 사용하자

4.일반 메서드 주입

@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;
 }
}
  • 한번에 여러필드를 주입 받을수 있다
  • 일반적으로 잘 사용 안한다

2.옵션 처리

  • 스프링 빈이 없이도 동작해야 할때가 있다

AutowiredTest

public class AutowiredTest {

    @Test
    void AutowiredOption(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(TestBean.class);

    }

    static class TestBean{

        @Autowired(required = false)
        public void setNoBean1(Member noBean1){
            System.out.println("noBean1 = " + noBean1);
        }

        @Autowired
        public void setNoBean2(@Nullable Member noBean2){
            System.out.println("noBean2 = " + noBean2);
        }

        @Autowired
        public void setNoBean3(Optional<Member> noBean3){
            System.out.println("noBean3 = " + noBean3);
        }
    }

}
  • @Autowired(required=false) : 자동주입할 대상이 없으면 수정자 메서드 자체가 호출안됨
  • org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null 입력
  • Optional<> : 자동 주입할 대상이 없으면 Optional.empty 가 입력

image


3.생성자 주입을 선택하라

  • 불변
    • 대부분 의존관계 주입은 한번 일어나면 애플리케이션 종료시점까지 의존관계를 변경할일이 없다
    • 수정자 주입을 사용할 경우 set 메서드를 열기때문에 누군가 실수로 변경할수 있다
    • 불변으로 설계하는것이 중요하다
  • 누락
    • 프레임워크 없이 순수한 자바코드를 단위테스트 하는경우

OrderServiceImpl

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;
    }
}

image

  • 생성자 주입을 넣으면서 누락된 부분을 확인할수 있다

OrderServiceImplTest

class OrderServiceImplTest {

    @Test
    void createOrder(){

        MemoryMemberRepository memoryMemberRepository = new MemoryMemberRepository();
        memoryMemberRepository.save(new Member(1L,"name", Grade.VIP));

        OrderServiceImpl orderService = new OrderServiceImpl(new MemoryMemberRepository(),new FixDiscountPolicy());
        Order itemA = orderService.createOrder(1L, "itemA", 10000);

        assertThat(itemA.getDiscountPrice()).isEqualTo(1000);
    }

}

생성자 주입방식 정리

  • 프레임워크에 의존하지않고 순수한 자바언어의 특징을 살리는 방법
  • 기본으로 생성자 주입을 사용하고 필수값이 아닌경우에는 수정자 주입방식을 옵션으로 부여
  • 기본으로 생성자 , 가끔 수정자 , 필드는 x

4.롬복과 최신트렌드

  • 개발하면 대부분 불변
  • 생성자 final 키워드를 사용하게 된다

롬복 build.gradle 추가

//lombok 설정 추가 시작
configurations {
 compileOnly {
 extendsFrom annotationProcessor
 }
}
//lombok 설정 추가 끝

//lombok 라이브러리 추가 시작
 compileOnly 'org.projectlombok:lombok'
 annotationProcessor 'org.projectlombok:lombok'
 testCompileOnly 'org.projectlombok:lombok'
 testAnnotationProcessor 'org.projectlombok:lombok'
 //lombok 라이브러리 추가 끝
  • plugin -> lombok 설치
  • annotation processors -> Enable annotation processing 클릭

기본코드 OrderServiceImpl

@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
    //private final DiscountPolicy discountPolicy = new FixDiscountPolicy();

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

}

롬복 OrderServiceImpl

@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
    //private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
}
  • final 필드가 있으면 자동으로 생성자를 만들고 @Autowired 넣어준다

5.@Autowired 필드명 , @Qualifier , @Primary

  • 조회대상 빈이 2개일때 해결방법
    • @Autowired 필드명
    • @Qualifier
    • @Primary

@Autowired 필드명

@Autowired
private DiscountPolicy discountPolicy
@Autowired
private DiscountPolicy rateDiscountPolicy
  • 필드명 매칭은 타입 매칭을 시도하고 결과에 여러빈이 있을경우 추가로 동작하는 기능

@Qualifier

  • 추가 구분자를 붙여주는 방식 , 빈이름을 변경하는것은 아니다
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}
  • @Qualifier 끼리 매칭
  • 빈이름 매칭
  • NoSuchBeanDefinitionException 예외 발생

@Primary

  • 우선순위를 정하는 방법
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class FixDiscountPolicy implements DiscountPolicy {}

우선순위

  • @Primary 는 기본값처럼 동작
  • @Qualifier 는 매우 상세하게 동작하므로 우선순위를 먼저 가진다

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

  • 편리한 자동기능을 기본으로 사용하자
  • @Component 뿐만아니라 @Controller , @Service , @Repository 처럼 일반적인 애플리케이션 로직을 자동으로 스캔
  • 스프링부트는 컴포넌트 스캔을 기본으로 사용
  • 자동빈 등록으로도 OCP , DIP 를 지킬수 있다

수동빈 사용은 언제?

  • 업무로직 빈 : 웹을 지원하는 컨트롤러 , 핵심비즈니스 로직 서비스 , 데이터 계층의 로직을 처리하는 리포지토리
  • 기술지원 빈 : 데이터베이스연결, 공통로그처리업무로직 지원할때
  • 업무로직 : 자동빈 등록
  • 기술지원로직 : 수동빈 등록

정리

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

참고

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/dashboard (스프링핵심기술)

728x90