티스토리 뷰
https://susuhan.notion.site/Spring-DI-6113d9eefba446c99413d1323abe9276
- 이쁘게 보기 -
Spring DI 방법론
A. 문제의 발단
코드 컨밴션이 존재하지 않아, 프로젝트 내에 코드의 일관성이 일치하지 않는 현상이 발생하였다.
그 중 하나로 의존성 주입하는 방식이 거론되어, 의존성 주입 방식의 차이와 실제 오류가 발생하는지를 알아보게 되었다.
의존성 주입 방식에는 크게 3가지가 존재한다.
- 필드 의존성
- 생성자 의존성
- setter 의존성
1. 필드 의존성 주입 (Field Injection)
public class A {
@Autowired
private AService aService;
}
2. setter 의존성 주입 (Setter based Injection)
public class A {
private AService aService;
@Autowired
public void setAService(AService aService){
this.aService = aService;
}
}
3. 생성자 의존성 주입 (Constructor based Injection)
// 의존성 주입 대상이 한 개일 경우, 생성자에 @Autowired 어노테이션을 붙이지 않아도 된다.
public class A {
private final AService aService;
public A(AService aService){
this.aService = aService;
}
}
// 의존성 주입 대상이 2개이상인 경우, 생성자에 @Autowired 어노테이션을 붙여야 한다.
public class A {
private final AService aService;
private final BService bService;
@Autowired
public A(AService aService, BSerivce bService){
this.aService = aService;
this.bService = vService;
}
}
B. 차이점: Bean 주입 순서
1. 필드 의존성 주입
- 주입 받으려는 빈의 생성자를 호출하여, 빈을 찾거나 빈 팩토리에 등록한다.
- 생성자 인자를 사용하는 빈을 찾거나 만든다.
- 필드에 주입한다.
2. Setter 의존성 주입
- 주입받으려는 빈의 생성자를 호출하여, 빈을찾거나 빈 팩토리에 등록한다.
- 생성자의 인자를 사용하는 빈을 찾거나 만든다.
- 주입하려는 빈 객체의 수정자(setter)를 호출하여 주입한다.
🟥 필드, Setter 의존성 방식은 런타임에서 의존성을 주입하여, 의존성을 주입하지 않아도 객체가 생성될 수 있다.
3. 생성자 의존성 주입
- 생성자의 인자에 사용되는 빈을 찾거나 빈 팩토리에서 만든다.
- 찾은 파라미터 빈으로 주입하려는 생성자를 호출한다.
🟥 객체가 생성되는 시점에 빈을 주입하여, 의존성 주입되지 않아 발생할 수 있는 NullPointerException을 방지한다.
C. 순환 참조
A에서 B를 호출하고, B에서 A를 호출하는 관계를 순환 참조라 한다. 이런 경우가 발생하는 일은 한 프로젝트 내에 관리해야하는 클래스가 많아지는 경우 실수로 발생하게 된다.
@Service
public class Aservice {
@Autowired
private Bservice bservice;
}
@Service
public class Bservice {
@Autowired
private Aservice aservice;
}
- 필드와 Setter 방식에서는 빈이 생성된 후에 참조를 하기 때문에 애플리케이션이 아무런 오류나 경고없이 구동된다. 실제 코드가 호출될 때까지 알 수 없다는 의미이다.
- 생성자를 통해 의존성 주입할 경우 BeanCurrentlyInCreationException이 발생한다. 순환참조뿐만 아니라 의존 관계에 내용을 외부로 노출 시킴으로 애플리케이션이 실행하는 시점에서 오류를 확인할 수 있다.
D. 코드의 깔끔함
1. 필드 의존성 주입
@Service
public class Aservice {
@Autowired
private Bservice bservice;
}
장점: 한 필드에 어노테이션을 붙임으로써 생성자를 만들지 않아 코드의 수가 줄어든다.
단점: 필드가 많아질 경우 한 줄씩 어노테이션이 추가적으로 붙게 된다.
2.생성자 주입과 Loombok
매번 생성자를 만들어서 주입하는 것이 코드의 깔끔함을 저해시킨다. 하지만 Lombok을 이용한다면 생성자를 자동으로 만들어준다.
@RequiredArgsConstructor
@Service
public class Aservice {
private final Bservice bservice;
}
RequiredArgsConstructor는 final로 선언된 필드를 가지고 생성자를 만들어준다.
E. final 사용
필드나 setter 방식을 이용시에 final 키워드를 사용할 수 없다.
final 키워드 사용 시 런타임 상에서 의존성이 변경되는 가능성을 제거해준다.
F. 실제 코드 실행 시 발생 상황 관찰하기
@Service
public class A {
@Autowired
B b;
}
@Service
public class B {
@Autowired
A a;
}
@Service
public class A {
private final B b;
public A(B b) {
this.b = b;
}
}
@Service
public class B {
private final A a;
public B(A a) {
this.a = a;
}
}
오류가 발생하지 않았다.
앞에서 한 말이 거짓말일까?
2.6.0 버전 특징
기본적으로 순환참조가 발생하지 않는다.
spring.main.allow-circular-references=true
필드 주입 = 속성 추가후 발생 시 순환참조 오류가 발생하지 않고 그대로 동작한다.
결론
- 2.6.0 버전 이후부터는 순환참조 오류를 미리 잡아주게 되었다. 순환참조 상의 오류때문에 필드, set방식을 안 쓸 이유는 없다.
- 생성자 의존성 주입 방식의 이점은 런타임 상에서 bean 객체가 변경되지 않는 것을 선호한다는 이유만 남은 것 같다. 하지만 런타임 상에서 bean 객체를 의도적으로 변경하는 사람이 존재할까? 하지만 이런 생각은 이전에 순환참조를 안 만들면 된다는 생각과 동일하기 때문에, 간단하게 막을 수 있다면 막아두는 것이 좋다고 생각한다.
- 코드의 깔끔함에 있어서 사람들의 견해가 다르다고 생각한다.
누군가는 어노테이션을 사용함에 있어 깔끔하다고 생각하고, 누군가는 어노테이션이 없는 것에 깔끔함을 느낄 수 있다.
만약에 생성자 주입이 직접 설정해야하는 번거로움이 걱정된다면 final이 있는 필드만 생성자를 만들어주는 @RequiredArgsConstructor를 사용하면 될 것 같다. - set방식도 구현하는 방식만 다르지 필드 의존성 주입과의 단점과 동일하여 위에 논한 것으로 충분하다고 생각한다.
'코딩 관련 > JAVA' 카테고리의 다른 글
Thymeleaf 자바스크립트 내 변수 저장 및 외부 Js 내 사용 (0) | 2023.06.03 |
---|---|
Spring Boot Security 5 - Oauth2.0 구글 로그인 - 2 - 작성 중 (0) | 2022.09.23 |
Spring Boot Security 5 - Oauth2.0 구글 로그인 (HTTP BASIC 탐방) - 1 (0) | 2022.09.17 |
스프링 시큐리티 Oauth2.0 트위치 적용 (0) | 2022.07.13 |
android appwidget/Home Screen Widget/Button Click Event에 관해 (0) | 2018.10.10 |