(SPRING) IoC 컨테이너 - @Autowired

(SPRING) IoC 컨테이너 - @Autowired

생성자 @Autowired

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

실행결과

me.choi.demospring51.BookRepository 타입에 해당하는 Bean을 찾을 수 가 없다.

@Repository 추가 - Bean등록

@Repository public class BookRepository { }

그래 인정! 생성자이기 때문에 BookService에 대한 빈을 생성하지 못하는구나.

Bookservice bookService = new BookService(bookRepository); -> 불가!

Setter @Autowired

bookRepository에 대한 Bean을 생성하지 않은 상태 (즉 @Repository 설정 안 함)

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

실행결과 위의 상황과 동일

그렇다면 생성자도 아닌 Setter인대 적어도 BookService에 대한 Bean은 생성되야하는 것이 정상 아닐까?

@Autowired를 작성하여서

의존성을 주입하려고 하기 때문이다.

이때 required = false를 주어 Optional로 만들어 버리면 된다.

@Service public class BookService { BookRepository bookRepository; @Autowired(required = false) public void setBookRepository(BookRepository bookRepository) { this.bookRepository = bookRepository; } }

@SpringBootApplication public class Demospring51Application { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(Demospring51Application.class, args); String[] beanDefinitionNames = run.getBeanDefinitionNames(); System.out.println(Arrays.toString(beanDefinitionNames)); } }

bookService에 대한 bean은 생성되고 bookRepository에 대한 bean은 생성되지 않았다.

Field @Autowired

@Service public class BookService { @Autowired(required = false) BookRepository bookRepository; }

경우의 수

해당 타입의 빈이 없는 경우

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

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

특정 타입의 빈이 여러 개인 경우

public interface BookRepository { }

@Repository public class MyBookRepository implements BookRepository{ }

@Repository public class JunwooBookRepository implements BookRepository{ }

이때 스프링은 어떤 BookRepository를 주입해줄까?

@Service public class BookService { @Autowired BookRepository bookRepository; }

주입을 못해준다.

스프링이 어떤 타입을 주입해야 할지 모르기 때문이다.

@Primary : 여러 가지 동일한 Bean을 주입해야 할 경우에 특정 빈을 사용할 것이라고 지정하는 애노테이션

@Repository @Primary public class JunwooBookRepository implements BookRepository{ }

확인 : BookService 빈 생성 시 어떠한 빈을 주입받는지

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

@Component public class BookServiceRunner implements ApplicationRunner { @Autowired BookService bookService; @Override public void run(ApplicationArguments args) throws Exception { bookService.printBookRepository(); }

실행결과

@Qualifier("small case의 클래스명") : 주입받을 빈을 명시하는 방법

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

실행결과

같은 타입의 모든 Bean을 주입받을 때 : List 선언

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

실행결과

필드 명을 주입받을 Bean의 small case를 작성하여 주입

@Service public class BookService { @Autowired BookRepository junwooBookRepository; public void printBookRepository() { System.out.println(this.junwooBookRepository.getClass()); } }

동작원리

BeanPostProcessor

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanPostProcessor.html

빈의 인스턴스를 생성한 다음 빈의 초기화 라이프사이클(initialization) 이전 이후에 또 다른 작업을 할 수 있는 라이프사이클 콜백이 있다.

이를 BeanPostProcessor라고 한다.

Bean 인스턴스가 만들어지고 나서 부가적인 처리를 하는 방법 두 가지

1. 애노테이션 사용

@PostConstruct public void setUp() { }

2. InitailiInitializingBean 구현

@Service public class BookService implements InitializingBean { @Autowired BookRepository junwooBookRepository; public void printBookRepository() { System.out.println(this.junwooBookRepository.getClass()); } @Override public void afterPropertiesSet() throws Exception { } }

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.html

즉 Bean 초기화 이전에

AutowiredAnnotationBeanPostProcessor 발동하여 @Autowired를 처리한다.

(빈의 주입)

@Service public class BookService { @Autowired BookRepository junwooBookRepository; // public void printBookRepository() { // System.out.println(this.junwooBookRepository.getClass()); // } @PostConstruct public void setUp() { System.out.println(this.junwooBookRepository.getClass()); } }

찍히는 곳이 다른데...?

이전의 라이프사이클은 애플리케이션 구동이 완료되고 처리되지만

현재의 라이프사이클은 애플리케이션 구동 중에 처리된다.

일반적인 Bean들에게

BeanPostProcessor의 애노테이션 처리 로직을 적용한다.

AutowiredAnnotationBeanPostProcessor가 빈으로 등록되어있는지 확인

@Component public class BookServiceRunner implements ApplicationRunner { @Autowired BookService bookService; @Autowired ApplicationContext applicationContext; @Override public void run(ApplicationArguments args) throws Exception { //bookService.printBookRepository(); AutowiredAnnotationBeanPostProcessor bean = applicationContext.getBean(AutowiredAnnotationBeanPostProcessor.class); System.out.println(bean); } }

실행결과

코드 참조

https://github.com/mike6321/Spring/tree/master/Spring/demospring51

from http://jwdeveloper.tistory.com/75 by ccl(A) rewrite - 2020-03-06 07:20:50

댓글

이 블로그의 인기 게시물

데이터 바인딩 추상화 - propertyEditor

[샤니마스 SPRING PARTY2020] THE IDOLM @ STER SHINY COLORS SPRING...

Spring Web Form