공부

[Spring] spring의 의존성 주입 방법 4가지

SunYerim 2025. 4. 4. 13:03

(기본적인 내용이지만, 복습겸 정리 -!)

우선, 스프링에서 의존성 주입(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