[Spring] @Autowired 란?

2020년 01월 12일 by Xion

    [Spring] @Autowired 란? 목차

 

@Service란, bean등록을 위해 명시해주는 애노테이션이다.

[ @Repository ] OR  [ @Component ]

->구분해서 사용하자  !

why ? 서비스에 bean 주입이 가능하며 애노테이션이 붙어있는 특정한 기능을 실행할 수 있고, AOP에서 사용하기에도 좋다.

 

Autowired란?

 

-필요한 의존 객체의 "타입"에 해당한느 빈을 찾아 주입한다.

 

[ @Autowired ]

- required : 기본값 : true

(못 찾으면 구동 실패 )

 

[ 사용할 수 있는 위치 ]

-생성자 (스프링 4.3부터는 생략 가능)

-setter 

- filed

 

[ 경우의 수 ]

-해당 타입의 빈이 없는 경우

-해당 타입의 빈이 한 개인 경우

-해당 타입의 빈이 여러개인 경우

  -> 빈 이름으로 시도,

       - 같은 이름의 빈 찾으면  해당 빈 사용

       - 같은 이름 못 찾으면 실패

 

[ 같은 타입의 빈이 여러개 일때 ]

- @Primary

- 해당 타입의 빈 모두 주입 받기

- @Qualifier (빈 이름으로 주입)

 

 

 

Autowired의 편리함

@Autowired 사용전

@Service
public class BookService {
​
    private BookRepository bookRepository;
​
    public BookService(BookRepository bookRepository){
        this.bookRepository = bookRepository;
    }
​
}

우선 BookService Class를 살펴보겠습니다. BookService Class는 생성자로 BookRepository를 전달받고 인스턴스 변수에 할당하는 코드를 가지고 있습니다.

 

<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설정을 해주어야 했습니다.

 

@Configuration
public class ApplicationConfig {    
    @Bean
    public BookRepository bookRepository(){
        return new BookRepository();
    }
    
    @Bean
    public BookService bookService(){
        return new BookService(bookRepository());
    }    
}

또는 위처럼 XML 설정 파일을 대신하는 Java Class에 Bean설정을 만들어주어 해결할 수도 있습니다.

 

 

 

!!하지만 

@Autowired 사용

@Service
public class BookService {
    private BookRepository bookRepository;
​
    @Autowired
    public BookService(BookRepository bookRepository){
        this.bookRepository = bookRepository;
    }    
}

하지만 @Autowired어노테이션을 사용하는 경우 위와 같이 객체의 의존성을 가지는 부분에 간단히 @Autowired 어노테이션을 사용하면 쉽게 의존성 주입을 받을 수 있게 됩니다.

 
@Repository
public class BookRepository { ... }

의존성 주입 타켓이 되는 Class 역시 당연히 Bean으로 등록이 되기위한 @Repository어노테이션이 부여되어 있는것을 주목해야 합니다.


 

@Autowired Setter에서 사용하기

 

 

setter 메소드에서도 @Autowired 어노테이션을 부여하여 사용할 수 있습니다. 위의 그림은 실제 @Autowired 어노테이션 인터페이스입니다. @Target을 보면 CONSTRUCTOR, METHOD, PARAMETER, FIELD, ANNOTATION_TYPE등에 부여가 가능한것이 보입니다. 우리는 여기서 @Autowired 어노테이션 인터페이스가 가지고 있는 required() 메소드에 주목해야 합니다. 이유는 예제를 통해 알아보도록 하겠습니다.

 

위에서 보았던 BookService Class입니다. 이번에는 생성자가 아닌 Setter메소드에 @Autowired어노테이션을 부여한 것을 볼 수있습니다. 앞서 말했듯이 METHOD에도 부여가 가능하다고 했으니 당연하겠죠? 중요한 사항은 지금부터 입니다.

 

public class BookRepository { ... }
// BookRepository Class의 Bean등록 어노테이션을 제거 해보겠습니다.

 

어?, 오류가 나네요? 아까전에 의존성 주입이 되는 클래스도 Bean으로 등록이 되어있어야 한다고 했으니, 당연하지 않냐구요? 다시 한번 생각해보아야 합니다. setter로 주입되는 의존성의 경우에는 생성자 처럼 필수적으로 의존성 주입이 되어야하는 것이 아닙니다, 필요에 의해서 주입이 되는것이지요. 그러면 왜 빨간줄이 그어지며 컴파일에 실패하게 될까요?

 

 

아까전에 살펴보았던 @Autowired어노테이션 인터페이스에서 보았던 required() 때문입니다. Default값이 True 되어있었기 때문이죠. required가 true인 경우에는 해당 의존성은 "꼭 필요한 대상이므로 주입을 반드시 받아야 한다"라는 의미가 되어버립니다.

그러면 required를 false로 설정해보겠습니다. Bean에 등록이 되어있지 않음에도 bookRepository를 Autowired한것을 볼 수 있습니다.

 

@Autowired의 생략

생성자에서 의존성을 주입받는 경우에는 @Autowired어노테이션을 생략하더라도 자동으로 의존성을 주입받을 수 있습니다. 

 why ??

->생성자의 매개변수로 객체를 전달받도록 되어있는 경우에는, 해당 해당 객체가 생성될 때에는 반드시 그 객체를 주입받아야 하기 때문입니다.

@Service
public class BookService {
    private BookRepository bookRepository;
    
    public BookService(BookRepository bookRepository){
        this.bookRepository = bookRepository;
    }
}

위와 같이 생성자에 BookRepository를 주입받는 경우에는 @Autowired가 없더라도 자동으로 주입을 받게 되어있습니다.

BookRepository의 Bean으로 등록되기 위한 어노테이션을 한번 제거

 

다시 BookService Class로 돌아와보면, @Autowired어노테이션이 부여되어 있지 않음에도 Autowired할 수 없다는 문구가 나타납니다.


다형성 @Autowired

@Autowired 대상이 되는 객체가 여러개가 될 수 있는 경우는 어떻게 될까요? 역시 예제를 통해 알아보도록 하겠습니다.

문제점

간단히 BookRepository Interface를 생성하고, 그것을 구현한 2개의 클래스(MyBookRepository, SecondBookRepository)를 생성했습니다.

@Repository
public class MyBookRepository implements BookRepository {
}
,
@Repository
public class SecondBookRepository implements BookRepository {
}

당연히 각각의 Class들은 Bean으로 등록되기 위한 Annotation을 가지고 있습니다. 이 경우에는 @Autowired에 의해 어떤 객체가 주입될까요?

 

BookService Class를 확인해보겠습니다. 이때에는 위와 같이 "한개 이상의 BookRepository Type 이 존재합니다."라는 오류메시지가 나타나며 컴파일에 실패하게 됩니다. 어찌보면 당연합니다 죽음의 다이아몬드와 비슷한 상황이 아닐까요?

 

해결법

1. @Primary

첫번째 해결 방법은 @Primary 어노테이션입니다.

간단합니다. @Autowired에 대상이 되는 클래스들 중 주입이 되길 원하는 클래스에 이 어노테이션을 부여하면 Spring이 자동으로 해당 객체를 주입하게 됩니다.

 

2. @Qualifier

@Service
public class BookService{
​
    @Autowired
    @Qualifier("myBookRepository")
    private BookRepository bookRepository;
​
    public void printBookRepository(){
        System.out.println(bookRepository.getClass());
    }
}

또는 @Autowired어노테이션으로 주입하는 곳에서 @Qualifier("bean id")를 이용해 어떤 Bean이 주입될지를 명시함으로써 해결할 수도 있습니다.

 

3. 모든 빈을 주입받기

@Service
public class BookService{
​
    @Autowired
    private List<BookRepository> bookRepository;
​
    public void printBookRepository(){
        this.bookRepository.forEach(System.out::println);
    }
}

마지막으로 모든 타입의 Bean을 주입받도록 할 수도 있습니다. @Autowired로 주입받는 객체의 형을 List로 받도록 합니다. 그렇게 된다면 해당 타입을 가지는 IoC컨테이너에 존재하는 모든 Bean이 List안에 들어가게 됩니다.