@Service
public class BookService {
@Autowired(required = false)
BookRepository bookRepository;
}
필드 주입에도 똑같이 옵션을 지정할 수 있다.
빈이 여러 개인 경우
@Service
public class BookService {
@Autowired
BookRepository bookRepository;
}
public interface BookRepository {
}
@Repository
public class MyBookRepository implements BookRepository {
}
@Repository
public class KeesunBookRepository implements BookRepository {
}
같은 BookRepository를 구현하는 클래스가 여러 개라면 BookService는 BookRepository를 주입할 수 없다.
@Primary
@Repository
@Primary
public class KeesunBookRepository implements BookRepository {
}
여러 개의 빈 중에 이 빈을 먼저 주입해달라고 할 때 사용한다.
class me.whiteship.autowired.KeesunBookRepository
ApplicationRunner를 활용해 주입된 빈을 출력하면 위와 같이 나오는 걸 확인할 수 있다.
@Qualifier
적용할 빈 이름을 @Qualifier에 추가해준다.
빈이 등록될 때 이름의 기본값은 클래스 이름의 앞 부분을 소문자로 바꾼 것이다.
@Service
public class BookService {
@Autowired
@Qualifier("keesunBookRepository")
BookRepository bookRepository;
public void printBookRepository() {
System.out.println(bookRepository.getClass());
}
}
@Repository
public class KeesunBookRepository implements BookRepository {
}
하지만 @Primary가 좀 더 type-safe하므로 @Primary를 추천한다.
해당 타입의 빈 모두 주입
@Repository
public class KeesunBookRepository implements BookRepository {
}
@Repository
public class MyBookRepository implements BookRepository {
}
@Service
public class BookService {
@Autowired
List<BookRepository> bookRepositories;
public void printBookRepository() {
this.bookRepositories.forEach(System.out::println);
}
}
@Service
public class BookService {
@Autowired
// 이름 지정
BookRepository myBookRepository;
public void printBookRepository() {
System.out.println(myBookRepository.getClass());
}
}
그래서 주입할 때 특정 클래스 이름을 그대로 쓰면 그 클래스가 주입된다. 하지만 이 방법은 추천하지 않는다.
동작 원리
BeanPostProcessor 라는 빈 라이프 사이클 인터페이스의 구현체에 의해 동작한다. 다음은 표준 빈 라이프 사이클이다.
즉, 11. postProcess**Before**Initialization 단계에서 @Autowired가 붙은 의존성을 주입한다.
PostConstruct
@Service
public class BookService {
@Autowired
BookRepository myBookRepository;
// 이제 Runner는 필요없다.
@PostConstruct
public void setup() {
System.out.println(myBookRepository.getClass());
}
}
@PostConstruct 단계에서는 이미 빈이 주입된 상태다.
12. InitializingBean's afterPropertiesSet 단계에서 작업한다.
이제 Runner 없이도 정보를 출력할 수 있다. 출력되는 위치만 조금 다르다.
2020-03-16 13:33:18.519 WARN 14600 --- [ main] o.s.boot.StartupInfoLogger : InetAddress.getLocalHost().getHostName() took 5005 milliseconds to respond. Please verify your network configuration (macOS machines may need to add entries to /etc/hosts).
2020-03-16 13:33:23.524 INFO 14600 --- [ main] me.whiteship.autowired.DemoApplication : Starting DemoApplication on macbook.local with PID 14600 (/Users/sojeong/dev/IntelliJ-workspace/spring-keesun-baik/spring-framework-core-02-autowired/target/classes started by sojeong in /Users/sojeong/dev/IntelliJ-workspace/spring-keesun-baik)
2020-03-16 13:33:23.525 INFO 14600 --- [ main] me.whiteship.autowired.DemoApplication : No active profile set, falling back to default profiles: default
2020-03-16 13:33:24.073 INFO 14600 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-03-16 13:33:24.079 INFO 14600 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-03-16 13:33:24.079 INFO 14600 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.31]
2020-03-16 13:33:24.121 INFO 14600 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-03-16 13:33:24.121 INFO 14600 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 566 ms
// @PostConstruct가 출력되는 위치
class me.whiteship.autowired.MyBookRepository
2020-03-16 13:33:24.219 INFO 14600 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-03-16 13:33:24.342 INFO 14600 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-03-16 13:33:24.347 INFO 14600 --- [ main] me.whiteship.autowired.DemoApplication : Started DemoApplication in 16.071 seconds (JVM running for 21.668)
// Runner가 출력되는 위치
Runner는 구동이 다 되었을 때 일을 한다.
반면, @PostConstruct는 12. InitializingBean's afterPropertiesSet에 해당한다.
정리
빈 팩토리가 BeanPostProcessor 타입의 빈을 찾는다.
그 중 하나인 AutowiredAnnotationBeanPostProcessor를 찾는다.
AutowiredAnnotationBeanPostProcessor가 일반적인 빈들에게 beanPostProcessor를 적용한다.
@Component
public class BookServiceRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
AutowiredAnnotationBeanPostProcessor bean
= applicationContext.getBean(AutowiredAnnotationBeanPostProcessor.class);
System.out.println(bean);
}
}