DI
각 클래스간의 의존관계를 빈 설정정보를 바탕으로 컨테이너가 자동으로 연결해주는것
Ex) Setter Injection, Constructor Injection, Field Injection
의존성을 주입하는 3가지 방법
spring은 @Autowired 어노테이션을 이용한 다양한 의존성 주입방법을 제공한다.
@Autowired 어노테이션은 타입(클래스)로 Bean을 지정한다.(생성자, 필드, 메소드 모두 적용한다)
의존성 주입은 필요한 객체를 직접 생성하는것이 아닌 외부로부터 객체를 받아 사용하는것이다
이는 객체간의 결합도를 줄이고 코드의 재활용성을 높이는 방법이 된다
@Autowired는 Spring에게 의존성을 주입하는 지시자 역할로 쓰인다.
1. 생성자 주입(Constructor Injection)
클래스의 생성자가 하나이고, 그 생성자로 주입받을 객체가 빈으로 등록되어 있다면 @Autowired를 생략할수 있다
@controller
public class CocoController {
// final을 붙일수 있다.
private final CocoService cocoservice;
public CocoController(CocoService cocoService) {
this.cocoServise = cocoService;
}
}
2. 필드 주입(Field Injection)
필드에 @Autowired 어노테이션만 붙여주면 자동으로 의존성이 주입된다.
사용법이 매우 간단
@Controller
public class CocoController {
@Autowired
private CocoService cocoService;
}
◑ 단점
- 외부에서 변경이 쉽지않다.
- 프레임워크에 의존적이고 객체지향적으로 좋지않다.
3. 수정자 주입(Setter Injection)
Setter 메소드에 @Autowired 어노테이션을 붙이는 방법이다.
@Controller
public class CocoController {
private CocoService cocoService;
@Autowired
public void setCocoService(CocoService cocoService) {
this.cocoService = cocoService;
}
}
◑ 단점
- 수정자 주입을 사용하면 setXXX 메소드를 public으로 열어두어야 한다. 때문에 언제 어디서든 변경이 가능하다.
추천 주입 방식
spring 에서 권장하는 방법은 생성자를 통한 주입이다.
이유를 알아보자
1. 순환 참조를 방지할 수 있다.
개발을 하다 보면 여러 컴포넌트 간에 의존성이 생긴다.
예를 들어 서로가 서로를 참조하는 상황을 그려보자
@Service
public class AService {
//순환 참조
private BService bService;
public void HelloA() {
bService.HelloB();
}
}
------------------------------------
@Service
public class BService {
//순환 참조
@Autowired
private AService aService;
public void HelloB() {
aService.HelloA();
}
}
필드 주입과 수정자 주입은 빈이 생성된 후에 참조를 하기 때문에 어플리케이션이 아무런 오류 그리고 경고 없이 구동이 된다. 그것은 실제 코드가 호출 될때까지 알수없다는게 큰 문제이다.
반면 생성자를 통해 주입하고 실행하면 BeanCurrentlyInCreationException이 발생한다.
즉, 의존관계 내용을 외부에 노출함으로써 어플리케이션을 실행하는 시점에서 오류를 체크 할 수 있다.
2. 불변성( Immutability)
생성자로 의존성을 주입할 때 final로 선언이 가능하다. 이로 인해 런타임에서 의존성을 주입받는 객체가 변할 일이 없게 된다.
하지만 수정자 주입이나 일반 메소드 주입을 이용하게 되면 불필요하게 수정의 가능성을 열어두게 되고 이는 OCP를 위반하게 된다.
그래서 생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장하는것이 좋다.
☆ 또한 필드 주입 방식은 null이 될 가능성이 높은 데 final은 null이 불가능 하다.
@Controller
public class CocoController {
private final CocoService cocoService;
public CocoController(CocoService cocoService) {
this.cocoService = cocoService;
}
}
3. 테스트에 용이하다.
생성자 주입을 사용하게 되면 테스트 코드를 좀 더 편리하게 작성 할 수있다.
public class CocoService {
private final CocoRepository cocoRepository;
public CocoService(CocoRepository cocoRepository) {
this.cocoRepository = cocoRepository;
}
public void doSomething() {
// do Something
}
}
public class CocoServiceTest {
CocoRepository cocoRepository = new CocoRepository();
CocoService cocoService = new CocoService(cocoRepository);
@Test
public void testDoSomething() {
cocoService.doSomething();
}
}
독립적으로 인스턴스화가 가능한 POJO를 사용하면 DI 컨테이너 없이도 의존성을 주입하여 사용할 수 있다.
이를 통해 코드 가독성을 높아지고, 유지보수가 용이하고 테스트의 격리성과 예측 가능성을 높일수 있다
'Spring' 카테고리의 다른 글
객체 지향 설계의 5가지 원칙(SOLID) (0) | 2023.05.07 |
---|---|
Bean (0) | 2023.03.22 |
IoC 컨테이너 (0) | 2023.03.22 |
Spring Security - Exception (0) | 2023.03.22 |
인증(Authentication) 과 인가(Authorization) (0) | 2023.03.22 |