(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
댓글
댓글 쓰기