의존관계 주입 방법
Table of Content
의존 관계 주입 방법
•
생성자 주입
•
수정자(setter) 주입
•
필드 주입
•
일반 메서드 주입
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;
}
...
Java
복사
다음 처럼 생성자(Constructor)를 통해서 의존 관계를 주입 받는 방법이다.
•
생성자 호출 시점에 딱 한번 호출되는 것이 보장
•
“불변”, “필수” 의존 관계에 사용
•
생성자가 딱 1개 있으면 위 @Autowired를 생략해도 자동 주입 된다.
2. 수정자 주입(setter)
@Component
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
//수정자 주입(setter)
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
Java
복사
필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법
•
“선택”, “변경” 가능성이 있는 의존관계에 사용
•
@Autowired 의 기본 동작은 주입 대상이 없으면 발생한다
◦
@Autowired(required = false)를 지정하면 된다.
3. 필드 주입
@Component
public class OrderServiceImpl implements OrderService{
@Autowired private MemberRepository memberRepository;
@Autowired private DiscountPolicy discountPolicy;
...
Java
복사
필드에 바로 @Autowired 로 주입하는 방식이다.
•
코드가 간결해 보이지만 외부에서 변경이 불가능해서 테스트하기 어려운 치명적인 단점
•
DI 프레임워크가 없으면 아무것도 할 수 없다
•
사용 X
◦
애플리케이션의 실제 코드와 관계없는 테스트코드
◦
스프링 설정 목적으로하는 @Configuration 같은 곳에서만 특별한 용도로 사용
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;
}
...
Java
복사
일반 메서드를 통해 주입 받을수도 있다.
•
한번에 여러 필드를 주입 받을 수 있다.
•
일반적으로 잘 사용되지 않음
•
보통 생성자 주입으로 해결되기 때문에 잘 사용되지 않음
옵션 처리
주입할 스프링 빈이 없어도 동작할 때가 있다. 그런데 @Autowired 만 사용하면 required 옵션이 기본값 ‘true’로 되어 있어 자동 주입 대상이 없으면 오류가 발생한다.
자동 주입 대상을 옵션으로 처리하는 방법은 다음과 같다.
package hello.core.autowired;
import hello.core.member.Member;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.lang.Nullable;
import java.util.Optional;
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);
}
}
}
Java
복사
스프링 빈이 아닌(빈 등록을 명시 하지 않은 클래스)인 Member 객체를 넣었다.
•
@Autowired(required = false) : 자동주입할 대상이 없으면 수정자 메서드 자체가 호출이 안된다.
•
@Nullable : 자동 주입할 대상이 없으면 null이 입력된다.
•
Optional<> : 자동 주입할 대상이 없으면 Optional.empty가 입력된다.
생성자 주입이 아닌 수정자 주입 등인 경우
@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;
}
...
Java
복사
만약 수정자 주입으로 OrderServiceImpl 이 주입되는 상황이면 이와 같이 NullPointerException이 발생한다.
•
createOrder는
◦
Member member = memberRepository.findById(memberId);
◦
int discountPrice = discountPolicy.discount(member, itemPrice);
•
처럼 repository와 discount 의존관계가 들어가있는 로직이기 때문 이 부분이 없다고 나타나는 오류이다.
package hello.core.order;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class OrderServiceImplTest {
@Test
void createOrder() {
OrderServiceImpl orderService = new OrderServiceImpl();
orderService.createOrder(1L, "itemA", 10000);
}
}
Java
복사
생성자 주입을 권장하는 이유
1. 불변
•
대부분 의존관계 주입은 한번 일어나면 애플리케이션 종료시점까지 의존관계를 변경할 일이 없다
•
대부분 의존관계는 변하면안된다.
•
수정자 주입을 사용하면 setter 메서드를 public으로 열어두므로 수정에 열린 설계 방법은 좋지 않다.
•
생성자 주입은 객체 생성시 딱 한번만 호출되므로 불변하게 설계 할 수 있다.
2. 누락방지
프레임워크 없이 순수한 자바 코드 단위테스트 하는 케이스가 많다. 이 때 생성자 주입을 통해서는 누락된 파라미터를 빠르게 확인(컴파일 시점)할 수 있다.
package hello.core.order;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemoryMemberRepository;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class OrderServiceImplTest {
@Test
void createOrder() {
MemoryMemberRepository memberRepository = new MemoryMemberRepository();
memberRepository.save(new Member(1L, "name", Grade.VIP));
OrderServiceImpl orderService = new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
Order order = orderService.createOrder(1L, "itemA", 10000);
assertThat(order.getDiscountPrice()).isEqualTo(1000);
}
// 생성자 주입으로는 OrderServiceImpl을 생성할때부터 필요한 파라미터를 명시해야 하도록한다.(필수)
// 생성자 주입 방식만이 필드에 초기화에 final(불변)을 넣을 수 있기 때문에 좋은 설계가 가능
}
Java
복사
Related Posts
Search