Using @Autowired

2023. 12. 10. 20:37Spring Framework/Spring IoC

Spring의 @Autowired 및 JSR-330 @Inject 완벽 정리 🚀

Spring에서는 @Autowired를 사용하여 빈(Bean)의 의존성을 자동으로 주입할 수 있습니다.
또한, JSR-330(javax.inject.Inject)을 사용하면 Spring에 종속되지 않고 표준적인 방식으로 DI(Dependency Injection)를 수행할 수도 있습니다.

📌 1️⃣ @Autowired@Inject 개념과 주요 특징

Spring IoC 컨테이너가 적절한 빈을 찾아 자동으로 주입
생성자, 필드, 세터, 일반 메서드에 적용 가능
같은 타입의 빈이 여러 개 존재하면 충돌 발생 가능@Qualifier 또는 @Primary 사용
Spring 4.3+ 부터 생성자가 1개만 있는 경우 @Autowired 생략 가능
JSR-330(@Inject)을 사용하여 Spring 의존성을 제거 가능
@Nullable, Optional<T>을 사용하여 주입할 빈이 없을 경우 null 처리 가능
Self Injection (자기 참조 주입) 지원우선순위가 가장 낮으며, 최후의 수단으로 사용해야 함
Spring @Autowired vs 표준 @Inject

  • @Autowired@Primary, required=false추가 기능 제공
  • @Inject는 Java 표준이므로 Spring 이외의 DI 프레임워크에서도 사용 가능
    Spring은 @Autowired, @Inject, @Value, @ResourceBeanPostProcessor를 통해 처리
  • 이 때문에 BeanPostProcessorBeanFactoryPostProcessor 내부에서는 사용할 수 없음
  • BeanPostProcessor 자체는 XML 설정이나 @Bean을 통해 명시적으로 설정해야 함

📌 2️⃣ @Autowired vs @Inject 비교

어노테이션 패키지 설명
@Autowired org.springframework.beans.factory.annotation Spring이 제공하는 자동 주입 기능
@Inject javax.inject (jakarta.inject) Java 표준의 의존성 주입(DI)

둘 다 기본적으로 동일한 기능을 수행하지만, 차이점이 있음

  1. @Autowired@Primary, required=false 등의 추가 기능을 제공
  2. @Inject는 Java 표준이므로 Spring이 아닌 다른 DI 프레임워크에서도 사용 가능

@Inject 사용 예제

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Inject
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
}

@Named@Component와 같은 역할을 하며, @Inject@Autowired와 동일한 역할을 수행

📌 3️⃣ @Autowired 적용 방법

1) 생성자 주입 (Constructor Injection)

가장 권장되는 방식이며, 불변성(immutability) 보장 및 테스트 용이

@Component
public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
}

✅ Spring 4.3 이후, 생성자가 1개일 경우 @Autowired 생략 가능

@Component
public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    // Spring 4.3+에서는 @Autowired 생략 가능
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
}

2) 세터 주입 (Setter Injection)

의존성을 선택적으로 주입할 때 유용하지만, 불변성이 보장되지 않음

@Component
public class MovieRecommender {

    private MovieFinder movieFinder;

    // 세터 주입
    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

3) 필드 주입 (Field Injection)

가장 간단하지만 테스트 및 유지보수 어려움권장되지 않음

@Component
public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;
}

🚨 필드 주입의 문제점

  • 외부에서 직접 주입할 방법이 없어 테스트하기 어려움
  • DI 프레임워크(SPRING)에 강하게 결합됨 → POJO 객체로 활용 어려움
  • 생성자 주입을 우선적으로 사용하는 것이 바람직함

📌 4️⃣ @Autowired 충돌 해결 (@Primary, @Qualifier)

1) @Primary (우선순위 설정)

같은 타입의 빈이 여러 개 있을 때 primary 빈을 지정

@Component
@Primary
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}

@Component
public class ComedyMovieCatalog implements MovieCatalog {
    // ...
}

2) @Qualifier (특정 빈 선택)

@Primary보다 더 명확하게 특정 빈을 지정할 때 사용

@Component
public class MovieService {

    private final MovieCatalog movieCatalog;

    @Autowired
    public MovieService(@Qualifier("comedyMovieCatalog") MovieCatalog movieCatalog) {
        this.movieCatalog = movieCatalog;
    }
}

📌 5️⃣ Self Injection (자기 참조 주입)

Spring 4.3+ 부터 @Autowired자기 참조(Self Injection) 를 지원합니다.

📌 하지만 자기 주입(Self Injection)은 우선순위가 가장 낮으며, 최후의 수단으로 사용해야 합니다.
📌 트랜잭션 프록시를 통해 같은 인스턴스 내의 다른 메서드를 호출할 때 유용

대안: 트랜잭션이 필요한 메서드를 별도의 Delegate Bean으로 분리

@Service
public class TransactionalTaskService {

    @Transactional
    public void performTransactionalTask() {
        System.out.println("Performing transactional task.");
    }
}

@Service
public class MyService {

    @Autowired
    private TransactionalTaskService transactionalTaskService; // 위임 빈 주입

    public void performService() {
        System.out.println("Main service logic execution started.");

        // 위임 빈을 통해 트랜잭션 작업 실행
        transactionalTaskService.performTransactionalTask();

        System.out.println("Main service logic execution completed.");
    }
}

📌 6️⃣ @Autowired, @Inject, @Value, @ResourceBeanPostProcessor

🚨 Spring의 @Autowired, @Inject, @Value, @ResourceBeanPostProcessor를 통해 처리됩니다.
📌 이 때문에 BeanPostProcessorBeanFactoryPostProcessor 내부에서는 사용할 수 없음

해결 방법

  • BeanPostProcessor 내부에서는 XML 설정이나 @Bean을 통해 명시적으로 빈을 등록해야 함
  • 예제:
@Configuration
public class AppConfig {

    @Bean
    public BeanPostProcessor myPostProcessor() {
        return new MyBeanPostProcessor();
    }
}

📌 7️⃣ Summary (요약)

@Autowired는 Spring이 제공하는 강력한 DI 기능
JSR-330 @Inject를 사용하면 Spring 종속성을 줄일 수 있음
@Primary, @Qualifier로 주입될 빈 선택 가능
@Autowired, @Inject, @Value, @ResourceBeanPostProcessor를 통해 처리됨 → BeanPostProcessor 내부에서는 사용할 수 없음 🚀

 

출처 : https://docs.spring.io/spring-framework/reference/core/beans/annotation-config/autowired.html

 

Using @Autowired :: Spring Framework

Only one constructor of any given bean class may declare @Autowired with the required attribute set to true, indicating the constructor to autowire when used as a Spring bean. As a consequence, if the required attribute is left at its default value true, o

docs.spring.io