(기본적인 내용이지만, 복습겸 정리 -!)
우선, 스프링에서 의존성 주입(Dependency Injection, DI)은 크게 4가지 방법으로 구분된다.
생성자 주입
- 생성자를 통해서 의존 관계를 주입하는 방법
- 생성자 주입은
생성자 호출시점에 딱 1번만 호출되는 것이 보장
된다. - 따라서 불변성을 가지는 의존성 주입에 적합하며, 반드시 필요한 의존관계에 사용된다.
- 이때, 크게
private final + 생성자
방식과@Autowired 기반 생성자
방식으로 나눌 수 있다.
private final
@Service
public class MyService {
private final MyRepository myRepository;
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
// Lombok을 사용할 경우
@Service
@RequiredArgsConstructor
public class MyService {
private final MyRepository myRepository;
// service logic
....
}
- final 키워드로 불변성 보장
- 의존성이 명시적으로 드러나며, 필수 주입임을 표현
- 테스트 시 Mock 객체 주입도 용이하고, 유지보수에도 유리함
- 리팩토링/유지보수 시 구조 파악이 쉬움
@Autowired
@Component
public class ServiceImpl implements Service {
private final MemberRepository memberRepository;
@Autowired // 생성자가 1개면 생략 가능
public ServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
- 생성자에
@Autowired
를 붙이면 Spring이 의존성 자동 주입 - 생성자가 2개 이상이면 반드시
@Autowired
로 명시해야 어떤 생성자에 주입할지 Spring이 인식 가능 - 생성자가 하나만 있으면
@Autowired
생략 가능 (Spring 4.3+)
@RequiredArgsConstructor
@Service
@RequiredArgsConstructor
public class MyService {
private final A a;
private final B b;
}
- Lombok이
final
또는@NonNull
필드를 인자로 받는 생성자를 자동 생성 - 생성자 주입 방식 그대로 따르므로
@Autowired
필요 없음 - 이때, 생성자를 직접 안 쓰고 롬복 어노테이션인 RequiredArgsConstructor을 사용하는 이유는 해당 클래스의 의존성 관계가 변경될 때마다 생성자 코드를 계속해서 수정하는 번거로움을 해결하기 위함이다.
setter 주입
@Component
public class MyService {
private MyRepository myRepository;
@Autowired
public void setMyRepository(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
- setter 메서드를 통해 의존성을 주입하는 방식
- 선택적 의존성, 혹은 주입 후 수정이 필요한 경우에 사용
- 장점: 선택적 의존성, DI 프레임워크 외 환경에서 주입 가능
- 단점: 불변성 보장 불가, 객체 생성 후 언제든 의존성 변경 가능성 존재
필드 주입
@Component
public class MyService {
@Autowired
private MyRepository myRepository;
}
- 필드에 바로 주입하는 방법
- 코드가 간결해지지만 외부에서 변경이 불가능하기때문에 테스트하기 어렵다는 단점이 있다.
- 순수 Java 환경이나 DI 컨테이너 외부에선 사용 불가
- 테스트 시 Mock 주입 어려워서 리플렉션 등 강제 접근 필요하다.
- 스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 목적으로 사용한다.
- 그렇지만…….. 웬만하면 사용하지 말자
일반 메서드 주입
@Component
public class MyService {
private A a;
private B b;
@Autowired
public void initDependencies(A a, B b) {
this.a = a;
this.b = b;
}
}
- 일반 메서드를 통해 주입하는 방법
- 한 번에 여러 필드를 주입받을 수 있다.
- 일반적으로 잘 사용하지 않는다.
생성자 주입을 사용하자 !!!!
생성자 주입 장점
- 순환 참조 방지
- 필드 주입과 수정자 주입은 먼저 빈을 생성한 뒤, 주입하려는 빈을 찾아서 주입한다.
- 그러나 생성자 주입은 먼저 빈을 생성하지 않고, 주입하려는 빈을 찾는다.
- 따라서 생성자 주입을 사용하면 서버 자체가 구동되지 않으므로 순환 참조가 실행되면서 방지할 수 있다.
- 테스트 용이
- 테스트하고자 하는 클래스에 필드 주입이나 수정자 주입으로 빈이 주입되어 있다면, Mockito를 통해 테스트를 진행한다.
- 생성자 주입은 단순히 원하는 객체를 생성하고, 생성자에 넣어주면 된다.
- final 선언 가능 = 불변성 보장
- 필드 주입과 수정자 주입은 final 선언이 불가능하다.
- 즉, 언제든 변경이 될 수 있다는 의미이다.
- 하지만, 생성자 주입은 final로 필드 선언이 가능하다. 즉 런타임에 객체 불변성을 보장한다.
- 명시성 & 가독성
- 유지보수 편의
'공부' 카테고리의 다른 글
[Spring] 순환참조문제 (0) | 2025.04.04 |
---|---|
[Spring] POJO란? Hibernate와 Spring이 왜 POJO 친화적인가 (0) | 2025.04.03 |
[DDD] Aggregate란? (0) | 2025.04.01 |
[JPA] JPA의 N + 1 문제 정리 (0) | 2025.03.27 |
[JPA] FetchType 정리 (0) | 2025.03.27 |