Dependency InjectionConstructor-based, Setter-based DI스프링 팀에서는 생성자 기반 의존성주입을 추천함@Autowired를 이용한 의존관계 자동주입의존관계 주입 시 해당 Bean 자리에 여러 개의 후보군이 있을 때(@Qualifier, @Primary)
Dependency Injection
- IoC는 다양한 방법으로 만들 수 있음. Dependency Injection은 IoC를 구현하는 하나의 패턴
- 전략 패턴
- 서비스 로케이터 패턴
- 팩토리 패턴
- 의존관계 주입패턴
- OrderService가 OrderRepository객체 생성할지 스스로 결정하지 않고 생성자를 통해서 주입을 받는 패턴을 생성자 주입 패턴 이라고 함
public class OrderService { private final VoucherService voucherService; private final OrderRepository orderRepository; public OrderService(VoucherService voucherService, OrderRepository orderRepository) { this.voucherService = voucherService; this.orderRepository = orderRepository; } }
Constructor-based, Setter-based DI
- 스프링에서 제안하기로는 둘이 섞어 쓸 때는, Constructor-based DI는 꼭 필요한 의존성들일 때 사용. setter based DI는 선택적인 의존성일 때 사용함
- Setter-based로 쓸 때 @Required annotation을 사용하면 필수적으로 알게 되지만, 필수적인 의존성일 때는 Constructor-based로 사용하기!
- Setter injection은 선택적 의존성일 때만 사용하기(그리고 합리적인 default value를 가져야 함. 그렇지 않다면 not-null check를 그 의존성을 사용하는 코드 마다 써주어야 하기 때문에)
- 장점 : 클래스의 객체를 reconfiguration가능하게 만들어 주고 re-injection을 후에 할 수 있도록 만들어 줌
스프링 팀에서는 생성자 기반 의존성주입을 추천함
- 초기화 시에 필요한 모든 의존관계가 형성되기 때문에 안전함(NullPointerException이 발생안함)
- 만약에 없을 수 있다면 Optional 로 설정
- 잘못된 패턴을 찾을 수 있게 도와줌(파라미터가 너무 많은게 바로 보임. refactoring해라!)
- 테스트를 쉽게 해줌
- Mock 객체를 한번에 생성하면서 넣어주고 테스트 하면됨. 필드기반이나 setter 기반의 의존성 주입을 이용하면 실수로 NPE 발생할 수 있음
- 불변성을 확보함(생성자 주입 이용시 final로 선언 가능함. setter나 field로 넣어주면 final 선언 불가) ⇒ multi thread 환경에서 thread-safety가 보장됨
@Autowired를 이용한 의존관계 자동주입
- 스프링은 ApplicationContext에 등록된 Bean을 코드에서 직접 주입하지 않고 생성자를 통해 주입하지 않고 자동으로 의존관계 형성해주는 기능이 있음 → Autowire
@Service public class VoucherService{ private final VoucherRepository voucherRepository; public VoucherService(VoucherRepository voucherRepository){ this.voucherRepository = voucherRepository; } } /// 의존 관계 자동 주입으로 변경하면 // 1. field 로 Autowired하는 방법 @Service public class VoucherService{ @Autowired // IoC 컨테이너에 의해 자동으로 주입이 됨 private VoucherRepository voucherRepository; } // 2. setter로 Autowire하는 방법 @Service public class VoucherService{ private VoucherRepository voucherRepository; @Autowired public void setVoucherRepository(VoucherRepository voucherRepository){ this.voucherRepository = voucherRepository; } }
- 원래는 생성자 주입도 Autowire이 붙었던 건데 스프링이 업데이트 되면서 안붙여도 알아서 해주게 변경이 된 것임.
- 만약에 생성자가 두개가 있다면 자동으로 주입해서 생성할 생성자 메서드에 @Autowired를 붙여줘야 함
- @Autowired 를 할 클래스가 Bean 등록이 안되어있다면 에러 발생하면서 탑 클래스 객체 자체 생성이 안됨
- @Autowired(required=false) 로 하면 생성 됨
의존관계 주입 시 해당 Bean 자리에 여러 개의 후보군이 있을 때(@Qualifier, @Primary)
public class VoucherService { private final VoucherRepository voucherRepository; public VoucherService(VoucherRepository voucherRepository) { this.voucherRepository = voucherRepository; } } // MemoryVoucherRepository 와 JdbcVoucherRepository 둘다 가 VoucherRepository를 // 상속받고 있으면 둘다 저 자리에 들어갈 수 있음
- @Primary를 붙여주어 우선 시 될 클래스를 정의 해줌
- @Qualifier(”memory”) @Qualifier(”jdbc”) 이렇게 해줄 수 있음(Qualifier는 사실 주로 안씀. 쓰는 쪽에서는 걱정 안하게 하는게 좋음)
- A서버 접속용 Jdbc템플릿, B 서버 접속용 jdbc템플릿 .. 이런 식으로 카프카나 이용할 때 동일한 경우가 여럿 생기는 경우가 존재할 수 있음. 이럴때 Qualifier를 주로 사용함
public VoucherService(@Qualifier("memory") VoucherRepository voucherRepository) { this.voucherRepository = voucherRepository; } var voucherRepository = BeanFactoryAnnotationUtils.qualifiedBeansOfType( applicationContext.getBeanFactory(), VoucherRepository.class, "memory");