Programming/Spring

[Spring] @Autowired의 다양한 사용 방법 - required, Primary, Qualifier

byeong07 2022. 5. 29. 23:09

1. 의존객체 타입의 빈이 없는 경우

다음과 같이 BookService 클래스와 BookRepository 인터페이스가 있다.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class BookService {
 
    BookRepository bookRepository;
 
    @Autowired
    public BookService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }
}
 
//
 
public interface BookRepository {
}
cs

 

BookService 클래스에는 @Service 애노테이션을 붙여 빈으로 등록해주었고 BookRepository는 애노테이션을 붙이지 않은 일반 자바 클래스이다.

BookRepository는 BookService의 의존객체이며 @Autowired를 붙여 BookService의 생성자를 통해 주입받도록 하였다.

 

이대로 실행하면 에러가 난다.

 

BookService 빈을 생성하려면 BookRepository 빈이 필요한데 IoC 컨테이너에서 해당하는 타입의 빈을 찾을 수 없으니 BookRepository를 빈으로 등록하라는 뜻이다.

 

코드를 바꿔 이번엔 setter로 주입받도록 해보자.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class BookService {
 
    BookRepository bookRepository;
 
    @Autowired
    public void setBookRepository(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }
}
 
//
 
public interface BookRepository {
}
cs

 

실행하면 역시 에러가 발생한다.

 

생성자로 주입받던 것과 달리 Setter로 주입받도록 했기때문에 원래는 BookService의 인스턴스는 생성할 수 있는게 맞지만 @Autowired 애노테이션에 의해 BookRepository를 주입받도록 설정한 BookService도 생성되지 않는 것이다.

 

이렇게 @Autowired 애노테이션을 처리하던 중 해당하는 빈의 타입을 못찾거나 의존성 주입을 할 수 없는 경우에는 에러가 발생하며 어플리케이션 구동이 제대로 되지 않는다.

 

@Autowired에 required 속성값을 추가해보자.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class BookService {
 
    BookRepository bookRepository;
 
    @Autowired(required = false)
    public void setBookRepository(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }
}
 
//
 
public interface BookRepository {
}
cs

 

이번엔 에러 없이 정상적으로 실행된다.

 

@Autowired(required = false)은 의존성을 'Optional'로 설정하는 것이다.

 

주입받을 의존객체가 필수적이지 않을 경우 @Autowired(required = false)로 설정해서 의존객체를 주입받지 못하더라도 빈을 생성하도록 할 수 있다.

 

결과적으로 BookService는 BookRepository를 주입받지 않은 상태로 IoC 컨테이너에 등록된다.

 

required 속성의 기본값은 true이기 때문에 지정하지 않을 경우 setter로 주입받게 했더라도 의존객체를 주입받지 못하면 항상 에러가 발생한다.

 

required = false는 @Autowired를 필드나 setter에 붙였을 경우에만 사용할 수 있다.

 

2. 의존객체 타입의 빈이 여러개인 경우

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class BookService {
 
    @Autowired
    BookRepository bookRepository;
}
 
//
 
public interface BookRepository {
}
 
//
 
import org.springframework.stereotype.Repository;
 
@Repository
public class MyBookRepository implements BookRepository {
}
 
//
 
import org.springframework.stereotype.Repository;
 
@Repository
public class AnotherBookRepository implements BookRepository {
}
cs

BookService에서 BookRepository를 필드로 주입받도록 변경한다.

MyBookRepository, AnotherBookRepository 클래스를 새로 만들고 BookRepository를 구현하도록 한다.

둘 다 @Repository를 붙여 빈으로 등록함으로써 BookRepository 타입의 빈이 두개가 되었다.

 

 

이 경우 spring은 BookService에 어떤 BookRepository 빈을 주입해줄까?

 

결과는 주입해 줄 수 없다.

두 BookRepository중 어떤 빈을 원하는지 spring 입장에서 알 수가 없기 때문이다.

 

1) @Primary

@Primary 애노테이션을 사용해서 해당하는 타입의 빈이 여러개일 경우 우선적으로 주입할 빈을 지정할 수 있다.

MyBookRepository에 @Primary 애노테이션을 붙이자.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class BookService {
 
    @Autowired
    BookRepository bookRepository;
}
 
//
 
public interface BookRepository {
}
 
//
 
import org.springframework.stereotype.Repository;
 
@Repository @Primary
public class MyBookRepository implements BookRepository {
}
 
//
 
import org.springframework.stereotype.Repository;
 
@Repository
public class AnotherBookRepository implements BookRepository {
}
cs

 

이렇게 하면 spring은 BookService에 BookRepository를 주입할때 여러개의 빈이 존재할 경우 @Primary 애노테이션이 붙어있는 빈을 주입해준다.

 

2) Qualifier

@Qualifier 애노테이션을 사용해서 빈 id를 지정해서 주입받을 수 있다.

다음과 같이 주입받는 곳에 @Qualifier 애노테이션을 추가하자.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class BookService {
 
    @Autowired @Qualifier("myBookRepository")
    BookRepository bookRepository;
}
 
//
 
public interface BookRepository {
}
 
//
 
import org.springframework.stereotype.Repository;
 
@Repository
public class MyBookRepository implements BookRepository {
}
 
//
 
import org.springframework.stereotype.Repository;
 
@Repository
public class AnotherBookRepository implements BookRepository {
}
cs

 

기본적으로 @Repository 애노테이션을 쓰면 빈 id는 소문자로 시작하는 클래스 이름과 동일하다.

따라서 MyBookRepository를 주입받도록 설정하려면 Qualifier 애노테이션에 myBookRepository라고 지정해주면 된다.

 

 

이렇게 @Primary와 @Qualifier 애노테이션은 같은 목적으로 쓸 수 있지만 @Primary가 더 type safe한 방법이다.

 

3) 해당하는 타입의 빈을 모두 주입받기

BookService에서 BookRepository를 주입받는 변수의 타입을 List로 변경한다.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
@Service
public class BookService {
 
    @Autowired
    List<BookRepository> bookRepositories;
}
 
//
 
public interface BookRepository {
}
 
//
 
import org.springframework.stereotype.Repository;
 
@Repository
public class MyBookRepository implements BookRepository {
}
 
//
 
import org.springframework.stereotype.Repository;
 
@Repository
public class AnotherBookRepository implements BookRepository {
}
cs

 

이렇게 하면 spring은 BookRepository 타입의 빈을 모두 주입해준다.

 

4) 빈 id와 변수명을 동일하게

별로 spring에서 추천하는 방법은 아니지만 @Autowired는 타입을 보고 나서 여러 개일 경우 변수명과 빈 id도 확인하는 과정을 거친다.

 

예를 들어 지금처럼 BookRepository 타입의 빈이 MyBookRepository, AnotherBookRepository가 존재하는 상태에서 MyBookRepository를 주입받고 싶으면 주입받을 변수명을 myBookRepository로 하면 된다.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class BookService {
 
    @Autowired
    BookRepository myBookRepository;
}
 
//
 
public interface BookRepository {
}
 
//
 
import org.springframework.stereotype.Repository;
 
@Repository
public class MyBookRepository implements BookRepository {
}
 
//
 
import org.springframework.stereotype.Repository;
 
@Repository
public class AnotherBookRepository implements BookRepository {
}
cs

 

여러 개의 빈 중에서 변수명과 빈 id가 동일한 빈이 주입된다.