의존관계 자동 주입
- 의존관계 주입은 크게 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 |
댓글
이 글 공유하기
다른 글
-
Spring Security
Spring Security
2021.12.14 -
빈 생명주기 & 콜백
빈 생명주기 & 콜백
2021.12.14 -
컴포넌트 스캔
컴포넌트 스캔
2021.12.12 -
싱글톤 컨테이너
싱글톤 컨테이너
2021.12.12