[Day 25 | Spring] @Autowired를 통한 자동 주입

y♡ding·2024년 11월 15일
0

데브코스 TIL

목록 보기
161/163

Using @Autowired
Autowiring Collaborators

자동 주입은 @Autowired 어노테이션을 통해 수행됩니다. 스프링은 이 어노테이션이 붙은 필드나 생성자, Setter 메서드에 자동으로 의존성을 주입합니다.

자동 주입(Autowired)은 스프링 프레임워크에서 객체 간의 의존성을 자동으로 주입해 주는 기능으로, 개발자가 의존성을 수동으로 설정하지 않아도 스프링 컨테이너가 필요한 객체를 자동으로 찾아 주입해 줍니다. 스프링의 DI(Dependency Injection) 기능의 일환으로, 코드를 더 간결하게 작성할 수 있고, 객체 간의 결합도를 낮출 수 있습니다.

1. @Autowired를 통한 자동 주입 방식

자동 주입 방식에는 필드 주입, Setter 주입, 생성자 주입이 있습니다.

1) 필드 주입 (Field Injection)

  • 직접 필드에 @Autowired 어노테이션을 사용하여 의존성을 주입합니다.
  • 코드가 간결하지만, 테스트에서 Mock 객체를 주입하기 어려워 권장되는 방식은 아닙니다.
@Component
public class OrderService {
    @Autowired
    private PaymentService paymentService;

    public void processOrder() {
        paymentService.pay();
    }
}

2) Setter 주입 (Setter Injection)

  • Setter 메서드에 @Autowired 어노테이션을 사용하여 의존성을 주입합니다.
  • 선택적으로 주입할 수 있는 객체가 있을 때 유용합니다.
@Component
public class OrderService {
    private PaymentService paymentService;

    @Autowired
    public void setPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void processOrder() {
        paymentService.pay();
    }
}

3) 생성자 주입 (Constructor Injection)

  • 생성자에 @Autowired 어노테이션을 사용하여 의존성을 주입합니다.
  • 필수 의존성을 주입할 때 권장되는 방식으로, 생성 시점에 모든 의존성이 주입되므로 불변 객체를 만들기 좋습니다.
  • 스프링 4.3 이후로는 생성자가 하나뿐인 경우 @Autowired 어노테이션을 생략할 수 있습니다.
@Component
public class OrderService {
    private final PaymentService paymentService;

    @Autowired
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void processOrder() {
        paymentService.pay();
    }
}

2. @Autowired의 작동 원리와 필요한 빈 탐색 방식

@Autowired 어노테이션을 사용하면, 스프링 컨테이너는 아래의 순서로 주입할 빈을 검색합니다.

  1. 타입으로 검색: 우선적으로 타입이 일치하는 빈을 검색합니다. 같은 타입의 빈이 하나뿐인 경우에는 해당 빈이 자동으로 주입됩니다.
  2. 빈 이름으로 검색: 동일한 타입의 빈이 여러 개 있는 경우, 필드나 파라미터의 이름과 같은 이름의 빈을 찾아 주입합니다.
  3. @Qualifier 사용: 동일한 타입의 빈이 여러 개 있을 때, 특정한 빈을 주입하고자 한다면 @Qualifier 어노테이션을 함께 사용하여 주입할 빈을 명시할 수 있습니다.

3. @Autowired와 @Qualifier 사용 예제

아래 예제는 같은 타입의 여러 빈 중에서 @Qualifier를 사용하여 특정 빈을 주입하는 방법입니다.

@Component
public class OrderService {
    private final PaymentService paymentService;

    @Autowired
    public OrderService(@Qualifier("creditCardPaymentService") PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void processOrder() {
        paymentService.pay();
    }
}
@Component("creditCardPaymentService")
public class CreditCardPaymentService implements PaymentService {
    // ...
}

@Component("paypalPaymentService")
public class PaypalPaymentService implements PaymentService {
    // ...
}

4. 자동 주입 시 발생할 수 있는 문제와 해결 방법

1) 동일한 타입의 빈이 여러 개 있을 때

  • 동일한 타입의 빈이 여러 개 있을 경우 어떤 빈을 주입해야 할지 명확하지 않으므로 @Qualifier*를 사용하여 해결할 수 있습니다.

2) 주입할 빈이 없을 때

  • 주입할 빈이 없으면 NoSuchBeanDefinitionException이 발생합니다.
  • 이를 방지하기 위해 @Autowired(required = false)로 설정하면, 빈이 존재하지 않을 때 주입을 생략할 수 있습니다.
@Autowired(required = false)
private PaymentService paymentService;

3) Optional 사용

  • Java 8 이상에서는 Optional을 활용하여 빈이 존재하지 않을 때 null 대신 빈값을 설정할 수 있습니다.
@Autowired
private Optional<PaymentService> paymentService;

5. 자동 주입의 장점

  • 코드 간결화: 의존성 주입을 위한 Setter나 생성자를 간단하게 작성할 수 있습니다.
  • 유연성: 인터페이스나 추상 클래스 타입을 사용하여 다양한 구현체를 주입할 수 있습니다.
  • 테스트 용이성: DI를 통해 Mock 객체를 주입할 수 있어 유닛 테스트가 용이합니다.

예제 코드

1. 필드 주입 (Field Injection)

예시 코드:

package org.example.di04;

import org.springframework.beans.factory.annotation.Autowired;

public class WriteAction {
    @Autowired
    private BoardDAO dao; // 필드에 직접 주입

    public WriteAction() {
        System.out.println("Write Action 생성자");
    }

    public void getDAO() {
        System.out.println("dao : " + dao); // Autowired 없으면 Null
    }
}

출력 결과:

Write Action 생성자
dao : org.example.di04.BoardDAO@<hashcode>

2. 생성자 주입 (Constructor Injection)

예시 코드:

package org.example.di04;

import org.springframework.beans.factory.annotation.Autowired;

public class WriteAction {
    private final BoardDAO dao;

    // 생성자에 @Autowired 추가
    @Autowired
    public WriteAction(BoardDAO dao) {
        System.out.println("WriteAction 생성자");
        this.dao = dao;
    }

    public void getDAO() {
        System.out.println("dao : " + dao);
    }
}

BeanConfig:

@Bean
public WriteAction writeAction(BoardDAO dao) {
    return new WriteAction(dao); // 생성자를 통해 주입
}

출력 결과:

WriteAction 생성자
dao : org.example.di04.BoardDAO@<hashcode>

3. Setter 주입 (Setter Injection)

예시 코드:

package org.example.di04;

import org.springframework.beans.factory.annotation.Autowired;

public class WriteAction {
    private BoardDAO dao;

    public WriteAction() {
        System.out.println("Write Action 생성자");
    }

    // Setter 메서드에 @Autowired 추가
    @Autowired
    public void setDao(BoardDAO dao) {
        System.out.println("setDao 호출");
        this.dao = dao;
    }

    public void getDAO() {
        System.out.println("dao : " + dao);
    }
}

BeanConfig:

@Bean
public WriteAction writeAction() {
    return new WriteAction(); // Setter로 의존성 주입
}
  • 설명:
    • Setter 메서드를 통해 BoardDAO 객체가 주입됩니다.
    • Setter 주입은 의존성이 선택적일 때 사용하기 적합합니다.

출력 결과:

Write Action 생성자
setDao 호출
dao : org.example.di04.BoardDAO@<hashcode>

4. 요약

주입 방식장점단점
필드 주입코드가 간결함. 테스트 외의 간단한 의존성 주입에 적합.테스트와 유지보수가 어려움. 의존성이 보이지 않으므로 디버깅 복잡.
생성자 주입필수 의존성을 강제 가능. 불변 객체에 적합. 단일 생성자의 경우 @Autowired 생략 가능.코드가 길어질 수 있음. 선택적 의존성 주입에는 부적합.
Setter 주입선택적 의존성에 적합. 테스트 시 Mock 객체 설정이 용이.필드가 명시적으로 초기화되지 않을 위험이 있음. 의존성을 강제하지 않음.

5. 활용 시 주의점

  1. 의존성을 명확히 설계하고, 불필요한 의존성은 제거합니다.
  2. 필수 의존성은 생성자 주입, 선택적 의존성은 Setter 주입을 사용하는 것이 권장됩니다.
  3. 빈이 스프링 컨테이너에서 관리되지 않을 경우, 의존성 주입이 이루어지지 않아 NullPointerException이 발생할 수 있습니다.

0개의 댓글

관련 채용 정보