• 의존관계 주입은 크게 4가지 방법이 있다.
    • 생성자 주입, 수정자 주입(setter 주입), 필드 주입, 일반 메서드 주입
  • 생성자 주입은 생성자 호출 시점에 딱 1번만 호출된다. 불변하다. 이와 대비되게 set으로 열어두면 변경사항이 발생하기 때문에 버그각 발생할 가능성이 있다.
  • 생성자가 1개 일 경우 @Autowired를 생략해도 자동 주입 된다.
  • Test 코드 작성 시 등록된 클래스 구체 타입을 AnnotationConfigApplicationContext에다 넘겨주고 getBean 으로 빈을 꺼내온 후에 테스트하고자 하는 구체 타입과 똑같은지 비교하게 된다. 
  • 선택적으로 변경 가능성이 있는 의존관계에서는 주입할때 @Autowired(required=false) 를 적용할 수 있다.
  • 자바빈 프로퍼티 규약을 수행하며 setXxx, GetXxx 으로 값을 읽거나 수정하는 규칙을 생성할 수 있다. 필드 값을 직접 변경하지 않는다. 
  • class Data {
     private int age;
     public void setAge(int age) {
     this.age = age;
     }
     public int getAge() {
     return age;
     }
    }
  • 스프링 컨테이너에서 빈을 등록하고 연관 관계를 자동 주입한다. 
  • @Autowired시 필드 주입의 경우에는 private 상관없이 주입할 수 있다. 
  • 필드 주입의 경우 외부에서 변경이 불가능해져서 테스트 하기 어렵다는 단점이 있다.
    • 순수 자바 객체를 기반으로 할때 DI 가 되지 않기 때문에 테스트에 어려움이 있다.
    • Spring Container가 관리하는것이 아니기 때문에 따로 setter를 만들어서 변경가능하도록 해야 한다.
  • 순수 자바 코드에서 @Autowired가 동작하지 않기 때문에 @SpringBootTest 를 작성해서 Spring Container를 통합한 환경에서 진행해야 한다. 스프링 관련 라이브러리들이 많이 따라와서 테스트 시 속도가 느려진다.
  • @Configuration에서는 설정 정보를 등록할 때 @Bean에 대한 의존관계를 주입해줘서 의존관계를 구성할 수 있다. 
  • 생성자 주입을 많이 사용한다. 의존관계 자동 주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다. 
    • 스프링 빈이 아닌 Member 클래스에서 @Autowired 코드를 적용해도 아무 기능이 동작하지 않는다. / 일반 자바 객체에서는 안된다.
  • @Autowired(required=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안됨
    org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력된다.
    Optional<> : 자동 주입할 대상이 없으면 Optional.empty 가 입력된다.
  • 아래 코드 처럼 선택적으로 주입하는것을 설정할 수 있다.
  • @Autowired(required = false)
    public void setNoBean1(Member member) {
     System.out.println("setNoBean1 = " + member);
    }
  • null이면 default 객체를 넣어줄 것으로 설정할 수 있다.
  • //null 호출
    @Autowired
    public void setNoBean2(@Nullable Member member) {
     System.out.println("setNoBean2 = " + member);
    }
  • 수정자 주입을 사용하게 되면 setXxx 메서드를 public 으로 열어두어야 한다. 이것은 누군가 실수로 변경할 수 있고, 변경하면 안되는 메서드를 열어두는 것은 좋은 설계 방법이 아니다.
  • 반면에, 생성자 주입은 객체를 생성할 때 딱 1번만 호출되기 때문에 이후에 호출되지 않는다. 불변하다.
    • 생성자 주입, 불변/누락을 막을 수 있다.
    • final 키워드를 사용해서 생성자에서 값이 설정되지 않는 오류를 컴파일 시점에서 막을 수 있다.
  • 좋은 전략은 생성자 주입을 선택하고, 옵션이 필요하면 수정자 주입을 진행한다. 필드 주입은 쓰지 않는것이 좋다. 
  • final 키워드를 사용하면 불변으로 유지할 수 있다. 생성자에 혹시라도 값이 설정되지 않는 오류를 컴파일 시 막아준다. 
  • 개발할 때 생성자, 주입 받은 값을 대입하는 코드도 만들어야 하는데 필드 주입을 좀 편하게 만들 수는 없을까? 
  • 이와 관련해서 lombok 을 사용한다. 롬복 라이브러리가 제공하는 @RequiredArgsConstructor 기능을 사용하면 final이 붙은 필드를 모아서 생성자를 자동으로 만들어준다.
  • @Component
    @RequiredArgsConstructor
    public class OrderServiceImpl implements OrderService {
     private final MemberRepository memberRepository;
     private final DiscountPolicy discountPolicy;
     //생성자가 없다.
    }
  • DiscountPolicy를 상속하는 FixDiscountPolicy, RateDiscountPolicy가 존재한다고 생각해보자. DiscountPolicy을 빈으로 등록해두면 꺼내올 때 FixDiscountPolicy, RateDiscountPolicy 어느것을 가져올 지 어려운 경우가 생긴다.
    • 이 경우 하위 타입으로 지정해서 bean을 꺼내오게 되면 DIP를 위반하게 된다. 그리고 유연성이 떨어진다.
    • 그래서 이 문제를 유연하게 해결하기 위해 3가지 방법이 존재한다.
    • 1. @Autowired를 통해서 이미 주입된 클래스 타입과 DI로 의존 주입해줄 파라미터와 매치된 정보를 확인한다.
    • 2. @Qualifier로 추가 구분자를 붙여준다.
    • 아래 코드 처럼 등록해두고
    • @Component
      @Qualifier("mainDiscountPolicy")
      public class RateDiscountPolicy implements DiscountPolicy {}
       
    • 아래 코드에서 보이는것처럼 지정된 @Qualifier와 매칭하여 꺼내온다.
    • @Autowired
      public OrderServiceImpl(MemberRepository memberRepository,
       @Qualifier("mainDiscountPolicy") DiscountPolicy
      discountPolicy) {
       this.memberRepository = memberRepository;
       this.discountPolicy = discountPolicy;
      }
    • @Qualifier는 @Qualifier끼리 매칭 하다가 없으면 빈 이름으로 매칭하고 이것마저 찾지못하면 NoSuchBeanDefinitionException 를 뿜게된다. 
    • 3. @Primary는 우선순위를 등록한다. @Autowired, @Primary를 같이 등록하면 @Primary가 등록된 것이 먼저 우선 등록되게 된다. 
    • @Qualifier 와 @Primary 기능에 대한 유즈 케이스는 아래를 읽어보면 도움이 된다.
    • 메인데이터베이스의 커넥션을 획득하는 스프링 빈은 @Primary 를 적용해서 조회하는 곳에서 @Qualifier
      지정 없이 편리하게 조회하고, 서브 데이터베이스 커넥션 빈을 획득할 때는 @Qualifier 를 지정해서
      명시적으로 획득 하는 방식으로 사용하면 코드를 깔끔하게 유지할 수 있다.
    • 조회 빈에 대해서 List,Map으로 받고 동적으로 빈을 다형성 코드를 유지하면서 사용할 수 있다.

'Spring' 카테고리의 다른 글

Spring Security  (0) 2021.12.14
빈 생명주기 & 콜백  (0) 2021.12.14
컴포넌트 스캔  (0) 2021.12.12
싱글톤 컨테이너  (0) 2021.12.12
객체 지향 원리 적용  (0) 2021.12.10