DI란 의존성 주입이라 부르며, 객체를 직접 생성하는 게 아닌 외부(IOC 컨테이너)에서 생성한 후 주입시켜주는 방식이다.
의존성 주입 방식으로는 3가지 방법이 존재
@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;
}
}
@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;
}
}
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
: 주입할 스프링 빈 없이도 동작해야 할 때가 있다. 그리고, @Autowired의 required=true라면 자동주입 대상이 없을 시 오류가 발생해버린다...
- @Autowired(required = false)
-> 수정자 메서드 자체가 호출 x , 주입할 대상이 없어도 동작 가능.- 매개변수에 @Nullable
-> 호출은 되지만 자동 주입 대상이 없으면 null이 입력- Optional<>
-> 자동 주입 대상이 없다면, Optional.empty 이 호출된다.
- 생성자 주입은 객체 생성 때 단 1번만 호출된다.
=>즉, 종료 시점까지 의존관계가 변경되지 않으며 다시 호출될 일이 없다.
- 오로지 생성자 주입만 사용 가능하며 값이 설정되지 않는 부분을 알려준다.
=> 컴파일 시점에 오류를 막을 수 있다.
더 간결해진 요즘 추세는...
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
이로써 롬복이 컴파일 시점에서 @RequiredArgsConstructor을 통해 final이 붙은 필드를 모아서 생성자 코드'를 자동으로 생성 한다.
+) 만약 @Qulifier로 주입하는데 @Qualifier("mainDiscountPolicy") 를 못 찾으면...
: @Autowired로 인해 여러 개의 빈이 나오게 된다면, 이 어노테이션을 통해서 우선권을 준다.
스프링은 1) 자동 < 수동
2) 넓은 범위 > 좁은 범위
3) @Primary < @Qualifier
: @Qualifier 물론 좋다.. 그러나 얘는 문자라 컴파일 시 '타입 체크'가 안되기에... 그냥 만들자
@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 {
}
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = 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);
}
}
jkijki12님의 글을 참조하여 작성하였습니다.