육감적 코딩

[Spring] 3. @Autowired 본문

정리/Spring

[Spring] 3. @Autowired

감감감감감감 2020. 7. 23. 22:19

매번 별 생각없이 사용했던 @Autowired 에 대해 정리해보았습니다.

 

 


 

 

@Autowire를 사용하려면 주입되는 객체를 빈으로 등록해야 한다.

 

 

하나의 인터페이스를 상속한 같은 타입의 여러 클래스가 있다면 해당 타입으로 @Autowire를 사용하려면 특정 방법을 사용해야한다.

 

방법으로는 크게 세가지가 있다.

 

방법1.  @Primary

 

@Autowire로 주입받을 때, @Primary가 달린 타켓을 주입해준다

 

확인방법.

 

ApplicationRunner을 implements 받아 컴포넌트로 등록하면, 실행시 해당 클래스의 run() 을 실행 시켜준다.

이를 활용하여 확인해 보자.

@Component
public class BookServiceRunner implements ApplicationRunner {

    @Autowired
    BookService bookService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        bookService.printBookRepository();
    }
}
@Service
public class BookService {
    //3가지 방법 추천
    @Autowired
    BookRepository bookRepository;

    public void printBookRepository(){
        System.out.println(bookRepository.getClass());
    }
}
public interface BookRepository {
}
@Repository @Primary
public class JshBookRepository implements BookRepository{
}
@Repository
public class MyBookRepository implements BookRepository {
}

 


 

방법2. @Qualifier

주입하려는 객체의 이름을 첫글자는 소문자로 작성해준다

@Qualifier("jshBookRepository") 하지만 타입세이프하지 못하기 때문에 위의 방법을 추천한다.

 


 

 

 

방법3. List형식

해당 타입과 일치하는 빈들을 List형식으로 주입받는다

 

@Service
public class BookService {
    //3가지 방법 추천
    @Autowired
    List<BookRepository> bookRepositories;
    
    public void printBookRepository(){
        this.bookRepositories.forEach(System.out::println);
    }
}

 


 

 

 

그외 방법

세가지 방식외에 한가지 더 방법이 있지만, 추천하는 방법은 아니다.

@Autowire는 사실 주입하는 타겟의 이름을 보고 주입하기도 한다.

@Service
public class BookService {
    //비추천
    @Autowired
    BookRepository myBookRepository;

    public void printBookRepository(){
        System.out.println(myBookRepository.getClass());
    }
}

이런식으로 필드명을 주입하려는 타켓의 이름을 소문자로 시작하는 이름으로 적어 주더라도 정상적으로 주입받을 수 있다.



 


 

@Autowire 의 원리 

 

BeanPostProcessor라는 라이프 사이클 인터페이스의 구현체에 의해서 동작하게됩니다.

 

BeanPostProcessor 란 ?

빈의 인스턴스를 만든다음에 빈의 초기화(initialization)라이프사이클이있는데, 그 라이프 사이클 이전 혹은 그 라이프사이클 이후에 어떤 부가적인 작업을 할수 있는 또다른 라이프사이클 콜백이있는데 그게 바로 BeanPostProcessor인터페이스 이다.

 

initialization 하는법

@PostConstruct 이런 애노테이션을 붙여서 정의 할 수도 있고, 

InitializingBean인터페이스를 구현하여 사용할 수도있다.

-BeanFactory 내용 참고-

PostPorcessBeforeInitialization~ 과 PostProcessAfterInitializatioin~ 을 확인 할 수 있다.

이 두가지의 메소드의 콜백을 제공해준다.

그중에서 우리는 AutowiredAnnotationBeanPostProcessor 을 사용하여 @Autowired 를 처리해줍니다.

(맞는 빈을 찾아서 주입을 해준다.)

 

언제? 

Initialize 전에 해준다.

PostPorcessBeforeInitialization~ 단계에서 해준다.

 

순서:

@Autowired 로 먼저 객체를 주입받음 → @PostConstruct 실행

 

해당 내용확인

@Service
public class BookService{
    @Autowired
    BookRepository myBookRepository;

    @PostConstruct
    public void setup(){
        System.out.println(myBookRepository.getClass());
    }
}

@PostConstruct 동작 단계에서 이미 @Autowired로 주입받은 myBookRepository를 사용할 수 있다.

 

기존에 찍히던 메시지 위치와 다른 위치에 찍히게 되는데 이유는 기존의 Runner는 구동이 끝나는 시점에 호출하여 시작하지만 해당방식의 라이프 사이클 콜백같은 경우는 위에 설명한 InitailizingBean’s afterPopertiesSet 단계에서 실행되기 때문이다.

 


 

 

동작 원리

BeanFactory가 BeanPostProcessor타입의 빈 들을 찾습니다. 그중에는 AutowiredAnnotationBeanPostProcessor 도 포함되어있습니다. 이를 이용하여 다른 일반적인 빈들에게  BeanPostProcessor을 적용하는 방식입니다.

@Component
public class MyRunner implements ApplicationRunner {

    @Autowired
    ApplicationContext applicationContext;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        AutowiredAnnotationBeanPostProcessor bean = applicationContext.getBean(AutowiredAnnotationBeanPostProcessor.class);
        System.out.println(bean);
    }
}

AutowiredAnnotationBeanPostProcessor 가 빈으로 등록되어있다는 걸 확인하는 방법.

 

Comments