@Autowired에 대한 분석이라기에는 얕은 지식을 가지고 @Autowired
어노테이션에 관해서 포스팅을 해보겠습니다.
1. Autowired란?
@Autowired
어노테이션은 이름을 보면 알 수 있듯이 이 어노테이션을 부여하면 각 상황의 타입에 맞는 IoC컨테이너 안에 존재하는 Bean을 자동으로 주입해주게 됩니다. 예제를 통해 알아보는것이 역시 좋을 것 같습니다.
2. Autowired의 편리함
@Autowired 사용전
public class BookService {
private BookRepository bookRepository;
public BookService(BookRepository bookRepository){
this.bookRepository = bookRepository;
}
}
우선 BookService Class를 살펴보겠습니다. BookService Class는 생성자로 BookRepository
를 전달받고 인스턴스 변수에 할당하는 코드를 가지고 있습니다.
xxxxxxxxxx
<bean id="bookRepository" class="com.keesun.spring.BookRepository"/>
<bean id="bookService" class="com.keesun.spring.BookService">
<constructor-arg name="bookRepository" ref="bookRepository"/>
</bean>
이때 원래의 경우라면 BookService에 BookRepository를 주입하기 위해서 XML파일에 다음과 같이 Bean설정을 해주어야 했습니다.
public class ApplicationConfig {
public BookRepository bookRepository(){
return new BookRepository();
}
public BookService bookService(){
return new BookService(bookRepository());
}
}
또는 위처럼 XML 설정 파일을 대신하는 Java Class에 Bean설정을 만들어주어 해결할 수도 있습니다.
@Autowired 사용
public class BookService {
private BookRepository bookRepository;
public BookService(BookRepository bookRepository){
this.bookRepository = bookRepository;
}
}
하지만 @Autowired
어노테이션을 사용하는 경우 위와 같이 객체의 의존성을 가지는 부분에
간단히 @Autowired
어노테이션을 사용하면 쉽게 의존성 주입을 받을 수 있게 됩니다.
public class BookRepository { ... }
의존성 주입 타켓이 되는 Class 역시 당연히 Bean으로 등록이 되기위한 @Repository
어노테이션이 부여되어 있는것을 주목해야 합니다.
3. @Autowired Setter에서 사용하기
setter 메소드에서도 @Autowired
어노테이션을 부여하여 사용할 수 있습니다. 위의 그림은 실제 @Autowired 어노테이션 인터페이스입니다. @Target
을 보면 CONSTRUCTOR, METHOD, PARAMETER, FIELD, ANNOTATION_TYPE
등에 부여가 가능한것이 보입니다. 우리는 여기서 @Autowired
어노테이션 인터페이스가 가지고 있는 required() 메소드에 주목해야 합니다. 이유는 예제를 통해 알아보도록 하겠습니다.
위에서 보았던 BookService
Class입니다. 이번에는 생성자
가 아닌 Setter
메소드에 @Autowired
어노테이션을 부여한 것을 볼 수있습니다. 앞서 말했듯이 METHOD
에도 부여가 가능하다고 했으니 당연하겠죠? 중요한 사항은 지금부터 입니다.
xxxxxxxxxx
public class BookRepository { ... }
BookRepository
Class의 Bean등록 어노테이션을 제거 해보겠습니다.
어?, 오류가 나네요? 아까전에 의존성 주입이 되는 클래스도 Bean으로 등록이 되어있어야 한다고
했으니, 당연하지 않냐구요? 다시 한번 생각해보아야 합니다. setter로 주입되는 의존성의 경우에는 생성자 처럼 필수적으로 의존성 주입이 되어야하는 것이 아닙니다, 필요에 의해서 주입이 되는것이지요. 그러면 왜 빨간줄이 그어지며 컴파일에 실패하게 될까요?
아까전에 살펴보았던 @Autowired
어노테이션 인터페이스에서 보았던 required()
때문입니다. Default값이 True
되어있었기 때문이죠. required가 true
인 경우에는 해당 의존성은 "꼭 필요한 대상이므로 주입을 반드시 받아야 한다"라는 의미가 되어버립니다.
그러면 required를 false로 설정해보겠습니다. Bean에 등록이 되어있지 않음에도 bookRepository를 Autowired한것을 볼 수 있습니다.
4. @Autowired의 생략
생성자에서 의존성을 주입받는 경우에는 @Autowired
어노테이션을 생략하더라도 자동으로 의존성을 주입받을 수 있습니다. 왜 그럴까요? 원리는 간단합니다. 생성자의 매개변수로 객체를 전달받도록 되어있는 경우에는, 해당 해당 객체가 생성될 때에는 반드시 그 객체를 주입받아야 하기 때문입니다.
public class BookService {
private BookRepository bookRepository;
public BookService(BookRepository bookRepository){
this.bookRepository = bookRepository;
}
}
위와 같이 생성자에 BookRepository를 주입받는 경우에는 @Autowired가 없더라도 자동으로 주입을 받게 되어있습니다. 어떻게 확인할 수있냐구요?
BookRepository의 Bean으로 등록되기 위한 어노테이션을 한번 제거해보세요.
다시 BookService Class로 돌아와보면, @Autowired
어노테이션이 부여되어 있지 않음에도 Autowired할 수 없다는 문구가 나타납니다.
5. 다형성 @Autowired
@Autowired
대상이 되는 객체가 여러개가 될 수 있는 경우는 어떻게 될까요? 역시 예제를 통해 알아보도록 하겠습니다.
문제점
간단히 BookRepository Interface
를 생성하고, 그것을 구현한 2개의 클래스(MyBookRepository, SecondBookRepository)를 생성했습니다.
public class MyBookRepository implements BookRepository {
}
,
public class SecondBookRepository implements BookRepository {
}
당연히 각각의 Class들은 Bean으로 등록되기 위한 Annotation
을 가지고 있습니다. 이 경우에는 @Autowired
에 의해 어떤 객체가 주입될까요?
BookService Class를 확인해보겠습니다. 이때에는 위와 같이 "한개 이상의 BookRepository Type 이 존재합니다."라는 오류메시지가 나타나며 컴파일에 실패하게 됩니다. 어찌보면 당연합니다 죽음의 다이아몬드
와 비슷한 상황이 아닐까요?
해결법
1. @Primary
첫번째 해결 방법은 @Primary
어노테이션입니다.
간단합니다. @Autowired
에 대상이 되는 클래스들 중 주입이 되길 원하는 클래스에 이 어노테이션을 부여하면 Spring이 자동으로 해당 객체를 주입하게 됩니다.
2. @Qualifier
xxxxxxxxxx
public class BookService{
"myBookRepository") (
private BookRepository bookRepository;
public void printBookRepository(){
System.out.println(bookRepository.getClass());
}
}
또는 @Autowired
어노테이션으로 주입하는 곳에서 @Qualifier("bean id")
를 이용해 어떤 Bean이 주입될지를 명시함으로써 해결할 수도 있습니다.
3. 모든 빈을 주입받기
public class BookService{
private List<BookRepository> bookRepository;
public void printBookRepository(){
this.bookRepository.forEach(System.out::println);
}
}
마지막으로 모든 타입의 Bean을 주입받도록 할 수도 있습니다. @Autowired
로 주입받는 객체의 형을 List
로 받도록 합니다. 그렇게 된다면 해당 타입을 가지는 IoC컨테이너에 존재하는 모든 Bean이 List안에 들어가게 됩니다.
'FrameWork > Spring' 카테고리의 다른 글
Spring - IoC 컨테이너의 기능 - 2 (Environment 란? , 분석 (Property, ConfigurableEnvironment, MutablePropertyResource)) (0) | 2019.04.12 |
---|---|
Spring - IoC 컨테이너의 기능 - 1 (Bean의 Scope) (0) | 2019.04.11 |
Spring - @ComponentScan 어노테이션이란? (2) | 2019.04.10 |
Spring - @(Annotation) 사용시 알 수 없는 에러 (0) | 2019.04.04 |
Spring - 사용자가 전달한 값 사용하기 - 7 (Command 객체, @RequestParam, @PathVariable) - 7 (2) | 2019.04.03 |