2024. 11. 14. 14:02ㆍSpring Framework/Spring IoC
아래 설명에서는 Spring의 Qualifier 활용과 @Autowired 주입 방식에 대해 자세히 살펴보고, 공식 문서 예제에 제시된 XML 기반 설정을 자바 기반 구성(Java Configuration)으로 변환하는 방법을 함께 안내합니다.
1. 개요: @Autowired와 @Qualifier
스프링에서 @Autowired는 타입(type) 중심으로 의존성을 주입합니다. 그러나 같은 타입의 빈(bean)이 여러 개 존재하면, 어떤 빈을 주입해야 할지 모호해집니다. 이 때,
- @Primary (또는 최근 버전에서는 @Fallback)
- @Qualifier 와 같은 추가적인 메커니즘으로 주입 대상을 결정할 수 있습니다.
그중 @Qualifier는 여러 후보 빈 중에서 특정 빈을 추가 식별자로 정교하게 선택하는 데 활용됩니다. 자바 기반 구성에서는 @Bean 메서드나 직접 @Component에 @Qualifier를 붙여서 빈을 등록한 뒤, 주입받는 쪽에도 @Qualifier나 커스텀 애노테이션을 붙여 일치시키는 방식으로 사용합니다.
2. 기본 예시: 간단한 @Qualifier 사용
2.1 예제 클래스
// MovieCatalog.java
public interface MovieCatalog {
// 영화 카탈로그 인터페이스
}
// SimpleMovieCatalog.java
public class SimpleMovieCatalog implements MovieCatalog {
// 실제 구현
}
// MovieRecommender.java
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// 혹은 컨스트럭터/메서드 주입 형태
/*
private final MovieCatalog movieCatalog;
@Autowired
public MovieRecommender(@Qualifier("main") MovieCatalog movieCatalog) {
this.movieCatalog = movieCatalog;
}
*/
}
2.2 자바 기반 구성 코드
공식 문서에서는 XML <bean> 태그 안에 <qualifier value="main"/> 또는 <qualifier value="action"/> 과 같이 기술했지만, 자바 기반 구성에서는 @Bean 메서드에 @Qualifier로 표현할 수 있습니다.
@Configuration
public class AppConfig {
// "main"이라는 식별자를 갖는 MovieCatalog 타입의 빈
@Bean
@Qualifier("main")
public MovieCatalog mainMovieCatalog() {
return new SimpleMovieCatalog();
}
// "action"이라는 식별자를 갖는 MovieCatalog 타입의 빈
@Bean
@Qualifier("action")
public MovieCatalog actionMovieCatalog() {
return new SimpleMovieCatalog();
}
// MovieRecommender는 @Autowired를 통해 mainMovieCatalog()를 주입받을 예정
@Bean
public MovieRecommender movieRecommender() {
return new MovieRecommender();
}
}
위 구성에서 @Qualifier("main")와 @Qualifier("action")을 붙인 @Bean들이 각각 SimpleMovieCatalog 빈을 생성합니다. MovieRecommender의 movieCatalog 필드는 @Qualifier("main")으로 지정된 빈이 주입됩니다.
3. 이름 매칭에 의한 자동 주입
Spring 6.1부터는 -parameters 컴파일 옵션이 활성화된 경우, 파라미터 이름이 빈 이름과 일치하면 자동으로 매칭을 시도합니다.
- @Qualifier나 @Primary(또는 @Fallback) 등이 없는 상황에서, 의존성 주입 대상이 모호하면 필드/파라미터 이름과 동일한 이름의 빈을 우선적으로 찾습니다.
예를 들어, 다음과 같이 movieCatalog라는 이름으로 @Autowired 필드를 선언했다면, 빈 이름이 movieCatalog인 것이 유일하게 존재하면 그 빈이 자동 주입될 수 있습니다.
@Configuration
public class AppConfigWithName {
@Bean
public MovieCatalog movieCatalog() {
return new SimpleMovieCatalog();
}
@Bean
public MovieRecommender movieRecommender() {
return new MovieRecommender();
}
}
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
// ...
}
위 경우 별도 @Qualifier 없이도, 필드 이름 movieCatalog와 동일한 빈 이름을 가진 @Bean이 존재하면 매칭이 이루어집니다.
4. 커스텀 Qualifier 애노테이션 사용
@Qualifier("값")만으로도 빈을 식별할 수 있지만, 좀 더 의미 있는 컨텍스트 정보를 주입하거나, 여러 속성값을 사용하고 싶을 때 커스텀 Qualifier 애노테이션을 만들 수 있습니다.
4.1 단일 값(value) 커스텀 Qualifier
아래와 같이 @Qualifier를 내장한 애노테이션을 정의할 수 있습니다.
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
이후 주입받는 코드에서 @Genre("Action")과 같이 사용합니다.
public class MovieRecommender {
@Autowired
@Genre("Action")
private MovieCatalog actionCatalog;
private MovieCatalog comedyCatalog;
@Autowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
this.comedyCatalog = comedyCatalog;
}
// ...
}
자바 기반 구성에서의 사용
XML 예제에서는 <qualifier type="Genre" value="Action"/> 등을 <bean> 태그 내부에 썼지만, 자바 설정에서는 @Bean에 직접 @Genre("Action")로 표기할 수 있습니다.
@Configuration
public class GenreConfig {
@Bean
@Genre("Action")
public MovieCatalog actionCatalog() {
return new SimpleMovieCatalog();
}
@Bean
@Genre("Comedy")
public MovieCatalog comedyCatalog() {
return new SimpleMovieCatalog();
}
@Bean
public MovieRecommender movieRecommender() {
return new MovieRecommender();
}
}
이렇게 하면, @Genre("Action")이 붙은 빈은 @Genre("Action")으로 주입 요청되는 곳으로 연결됩니다. @Genre("Comedy")가 붙은 빈은 @Genre("Comedy")로 주입됩니다.
4.2 값(value) 없는 커스텀 Qualifier
어떤 경우에는 단순히 오프라인용 온라인용 등과 같이 존재 자체로 의미를 부여하는 커스텀 애노테이션을 쓸 수 있습니다. 예를 들어,
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
}
그리고 주입받는 코드:
public class MovieRecommender {
@Autowired
@Offline
private MovieCatalog offlineCatalog;
// ...
}
자바 기반 구성
XML 예시에서는 <qualifier type="Offline"/>라고만 작성했지만, 자바 기반 구성에서는 다음과 같이 @Bean에 @Offline만 달면 됩니다.
@Configuration
public class OfflineConfig {
@Bean
@Offline
public MovieCatalog offlineCatalog() {
return new SimpleMovieCatalog();
}
@Bean
public MovieRecommender movieRecommender() {
return new MovieRecommender();
}
}
5. 여러 속성을 가진 커스텀 Qualifier
하나의 커스텀 애노테이션에서 여러 속성값을 가질 수도 있습니다. 예를 들어, 다음과 같이 정의해 보겠습니다.
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
String genre();
Format format();
}
public enum Format {
VHS, DVD, BLURAY
}
주입받는 클래스:
public class MovieRecommender {
@Autowired
@MovieQualifier(format = Format.VHS, genre = "Action")
private MovieCatalog actionVhsCatalog;
@Autowired
@MovieQualifier(format = Format.VHS, genre = "Comedy")
private MovieCatalog comedyVhsCatalog;
@Autowired
@MovieQualifier(format = Format.DVD, genre = "Action")
private MovieCatalog actionDvdCatalog;
@Autowired
@MovieQualifier(format = Format.BLURAY, genre = "Comedy")
private MovieCatalog comedyBluRayCatalog;
// ...
}
자바 기반 구성
공식 문서 예제에서는 <qualifier type="MovieQualifier"><attribute key="format" value="VHS"/><attribute key="genre" value="Action"/></qualifier> 와 <meta key="..."/> 등을 혼용했습니다. 자바 기반 구성으로 바꾸면 다음과 같이 매우 간단히 표현할 수 있습니다.
@Configuration
public class MultiAttributeQualifierConfig {
// VHS & Action
@Bean
@MovieQualifier(format = Format.VHS, genre = "Action")
public MovieCatalog actionVhsCatalog() {
return new SimpleMovieCatalog();
}
// VHS & Comedy
@Bean
@MovieQualifier(format = Format.VHS, genre = "Comedy")
public MovieCatalog comedyVhsCatalog() {
return new SimpleMovieCatalog();
}
// DVD & Action
@Bean
@MovieQualifier(format = Format.DVD, genre = "Action")
public MovieCatalog actionDvdCatalog() {
return new SimpleMovieCatalog();
}
// BLURAY & Comedy
@Bean
@MovieQualifier(format = Format.BLURAY, genre = "Comedy")
public MovieCatalog comedyBluRayCatalog() {
return new SimpleMovieCatalog();
}
@Bean
public MovieRecommender movieRecommender() {
return new MovieRecommender();
}
}
이렇게 @MovieQualifier(...)로 두 가지 속성을 모두 지정해두면, 주입받을 때 역시 같은 속성의 값들이 모두 일치해야 주입이 이뤄집니다.
6. @Resource와의 비교
- @Resource(JSR-250)는 디폴트로 “이름(name)으로 매칭”하는 애노테이션입니다. 즉, 빈의 고유 이름으로 주입할 때 적합하고, 타입 매칭은 고려되지 않습니다(다만 동일 타입 발견 시 우선순위 모호성은 발생할 수 있음).
- 반면 @Autowired는 “타입으로 매칭” 후, 추가로 @Qualifier의 값이나 빈 이름을 사용해 후보를 필터링하는 방식입니다.
단순히 “이 빈 이름을 정확히 주입해야 한다”라고 하면 @Resource(name="…") 방식이 더 직관적일 수 있으나, 여러 타입 혹은 Qualifier를 활용하는 구조라면 @Autowired+@Qualifier가 더 유연합니다.
추가로 @Autowired는 컨스트럭트나 여러 아규먼트를 갖는 메서드에도 사용 가능합니다. 반면 @Resource는 필드 또는 setter 메서드에만 적용되며, 컨스트럭터 주입과는 호환되지 않습니다.
7. 요약
- 디폴트로 @Autowired는 타입 매칭을 통해 의존성을 주입한다.
- 빈이 여러 개 존재하면,
- @Primary(또는 스프링 6.0+에서는 @Fallback)
- @Qualifier("…")
- 파라미터/필드 이름 매칭 등의 방식으로 모호성을 제거한다.
- @Qualifier에 문자열 값을 주어 해당 식별자를 기준으로 후보 빈을 좁히거나, 커스텀 애노테이션을 만들어 좀 더 의미 있는 키(key)들을 활용할 수 있다.
- 자바 기반 설정에서는 @Bean 메서드에 @Qualifier(또는 커스텀 애노테이션)를 적용하여 XML <qualifier> 엘리먼트와 동일한 효과를 낼 수 있다.
- @Resource는 디폴트로 빈 이름 매칭(고유 식별자) 위주로 동작하며, 컨스트럭터 주입이나 멀티 아규먼트 메서드 주입에는 쓰이지 않는다.
정리하자면, XML 설정에서의 <qualifier>와 <meta> 등을 사용하던 부분을 자바 기반 구성에서는 @Bean 메서드에 @Qualifier 또는 커스텀 Qualifier를 붙여 간단하게 대체할 수 있습니다. 특히, 여러 속성이 필요한 경우에도 XML의 <attribute> 혹은 <meta> 태그 대신, 자바 애노테이션 속성으로 선언함으로써 더욱 직관적이고 간결하게 관리할 수 있습니다.
위 예제들을 참고하여 Java Configuration 환경에서 원하는 대로 Qualifier를 세밀하게 지정하고 주입받는 로직을 구성하시면 됩니다.
'Spring Framework > Spring IoC' 카테고리의 다른 글
Basic Concepts: @Bean and @Configuration (0) | 2024.11.14 |
---|---|
Java-based Container Configuration (1) | 2024.11.14 |
Annotation-based Container Configuration (0) | 2024.11.14 |
Bean Scopes (0) | 2024.11.14 |
Method Injection (0) | 2024.11.14 |