Spring에서 @Autowired는 Spring 컨테이너가 관리하는 Bean을 자동으로 주입해주는 어노테이션입니다. Bean이 필요한 클래스에 @Autowired를 통해 객체를 주입할 수 있으며, 이때 Constructor Injection, Setter Injection, Field Injection 방식으로 주입할 수 있습니다. 또한 여러 Bean 중 특정 Bean을 주입할 때는 @Primary와 @Qualifier 어노테이션을 활용합니다.
장점:
@Service
public class MyService {
private final MyRepository myRepository;
@Autowired // 생성자 주입
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
장점:
사용 예:
@Service
public class MyService {
private MyRepository myRepository;
@Autowired // Setter 주입
public void setMyRepository(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
장점:
단점:
사용 예:
@Service
public class MyService {
@Autowired // 필드 주입
private MyRepository myRepository;
}
추천: 일반적으로 생성자 주입이 가장 권장되는 방식입니다. 생성자 주입은 객체를 불변하게 유지하며 테스트에 용이하기 때문에 스프링에서 권장하는 주입 방법입니다. 필드 주입은 주로 테스트를 위해 간단하게 사용하거나, 기존 코드에 간단히 Bean을 추가할 때 유용하지만, 새로운 코드에서는 되도록 지양하는 것이 좋습니다.
Spring 컨텍스트에 동일한 타입의 여러 Bean이 있는 경우, 주입 시점에 어떤 Bean을 사용할지 모호해질 수 있습니다. 이를 해결하기 위해 @Primary와 @Qualifier를 사용할 수 있습니다.
사용 예:
@Repository
@Primary // 우선적으로 선택될 Bean
public class PrimaryRepository implements MyRepository {
// 구현 코드
}
@Repository
public class SecondaryRepository implements MyRepository {
// 구현 코드
}
사용 예:
@Repository("primaryRepository")
public class PrimaryRepository implements MyRepository {
// 구현 코드
}
@Repository("secondaryRepository")
public class SecondaryRepository implements MyRepository {
// 구현 코드
}
@Service
public class MyService {
private final MyRepository myRepository;
@Autowired
public MyService(@Qualifier("secondaryRepository") MyRepository myRepository) {
this.myRepository = myRepository;
}
}
| 주입 방식 | 설명 | 장점 | 단점 |
|---|---|---|---|
| Constructor Injection | 생성자를 통해 주입 (가장 권장되는 방식) | 불변성 보장, 테스트 용이 | 의존성이 많아질 경우 생성자 코드가 길어질 수 있음 |
| Setter Injection | Setter 메서드를 통해 주입 | 선택적 주입 가능, 유연성 | 불변성 약화, 테스트 시 추가 설정 필요 |
| Field Injection | 필드에 직접 주입 (간편하지만 비권장) | 코드 간결화 | 테스트 어려움, 캡슐화 약화 |
이러한 주입 방식과 어노테이션을 활용하면 다양한 상황에서 유연하게 의존성을 주입할 수 있으며, 코드의 가독성과 유지보수성을 높일 수 있습니다.
Constructor Injection이 권장되는 이유:
Field Injection을 사용할 수 있는 경우:
간단한 테스트 클래스나 임시 데모 코드에서 빠르게 Bean을 주입해야 할 때 간단하게 사용할 수 있습니다.
기존 코드에 작은 수정이 필요하거나, 주입할 Bean이 많지 않아 생성자나 세터 주입이 불필요하게 복잡해지는 경우에 사용하기도 합니다.
단, 실제 애플리케이션 코드에서는 생성자 주입을 사용하는 것이 더 권장되며, 필드 주입은 주로 테스트 코드에서 간단히 사용됩니다.
Repository 인터페이스 정의:
public interface MyRepository {
String getData();
}
PrimaryRepository와 SecondaryRepository 구현 클래스 생성:
@Repository
@Primary // 기본 우선순위 설정
public class PrimaryRepository implements MyRepository {
@Override
public String getData() {
return "Primary Repository Data";
}
}
@Repository("secondaryRepository") // @Qualifier에 사용할 Bean 이름 설정
public class SecondaryRepository implements MyRepository {
@Override
public String getData() {
return "Secondary Repository Data";
}
}
@Qualifier 사용 예제:
@Service
public class MyService {
private final MyRepository myRepository;
// @Qualifier를 사용하여 secondaryRepository를 명시적으로 주입
@Autowired
public MyService(@Qualifier("secondaryRepository") MyRepository myRepository) {
this.myRepository = myRepository;
}
public String getData() {
return myRepository.getData();
}
}
테스트 실행:
@Qualifier와 Map을 활용한 동적 주입:
@Service
public class MyService {
private final Map<String, MyRepository> repositoryMap;
@Autowired
public MyService(Map<String, MyRepository> repositoryMap) {
this.repositoryMap = repositoryMap;
}
public MyRepository getRepository(String beanName) {
return repositoryMap.get(beanName);
}
}
이 코드에서 repositoryMap.get("beanName")을 통해 동적으로 Bean을 선택할 수 있습니다. 예를 들어, "primaryRepository"나 "secondaryRepository"를 키로 전달하여 해당 Bean을 얻을 수 있습니다.
ApplicationContext 활용:
@Service
public class MyService {
private final ApplicationContext applicationContext;
@Autowired
public MyService(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public MyRepository getRepository(String beanName) {
return applicationContext.getBean(beanName, MyRepository.class);
}
}
이 두 방법 모두 유연하게 Bean을 선택할 수 있어 상황에 따라 알맞은 Bean을 주입하여 사용할 수 있습니다.