스프링 핵심 기술-01.스프링 IoC 컨테이너와 빈/ApplicationContext과 다양한 빈 설정 방법
IoC 컨테이너 1부 : 스프링 IoC 컨테이너와 빈
앞 장에서 IoC 컨테이너 관련 부분들과 상당 부분 겹치는 부분들이 많아 간략하게만 정리하고 넘어가려고 한다.
이전포스트 : Spring Triangle-01.스프링 IoC
스프링 IoC 컨테이너에는 가장 중요한 인터페이스가 있다.
빈팩토리와 애플리케이션 컨텍스트.
사실 애플리케이션 컨텍스트가 빈팩토리를 상속받기 때문에 빈팩토리의 역할도 하고,
추가적으로 이벤트 퍼블리셔, 인바이러먼트, 빈팩토리, 메세지 소스 등 다른 인터페이스를 구현하고 활용한다.
IoC 컨테이너 장에서는 애플리케이션 컨텍스트가 구현하는 빈팩토리 외 이런 다른 인터페이스들을 구현하는 것을 정리하려고 한다.
BeanFactory
- 애플리케이션 컴포넌트의 중앙 저장소이다.
- 빈 설정 소스로 부터 빈 정의를 읽어들이고, 빈을 구성하고 제공한다.
빈(Bean)
- 스프링 IoC 컨테이너가 관리 하는 객체.
- 장점
- 의존성 관리
- 스코프
- 싱글톤: 하나의 객체
- 프로토 타입: 매번 다른 객체
IoC 컨테이너가 관리하는 빈들은 라이프 사이클 인터페이스를 지원한다.
ex. @PostConstruct 등
부가적인 작업 또는 스프링 자체에서 라이프사이클 콜백을 사용해서 부가적인 기능을 만들어 낼 수 있다.
@PostConstruct 참고
1.의존성 관리 만약 북 서비스가 의존하는 북 레파지토리가 널을 리턴한다면?
public class BookRepository {
public Book save(Book book) {
return null;
}
}
public class BookService {
// 1. 직접 생성하는 경우 (단위테스트 힘듬)
private BookReposotitory bookReposotitory = new BookReposotitory();
// 2. 의존성 주입 받는 경우
public BookService(private BookReposotitory bookReposotitory) {
this.bookReposotitory = bookReposotitory;
}
public Book save(Book book){
//로직 book.setBookStatus(BookStatus.DRAFT);
return bookReposotitory.save(book); // BookReposotitory에서 null 반환해서 무조건 null 반환한다.
}
}
- 직접 생성하는 경우 (단위테스트 힘듬) 의존성을 가진 북서비스의 단위 서비스 테스트 만들기가 어렵다.
@Mock
BookReposotitory bookReposotitory;
@Test
public void save(){
Book book = new Book();
when(bookRepository.save(book)).thenReturn(book); // null 일경우 동일한 book 인스턴스 return
// (북서비스에서 변경된 북 - 가짜 목업 객체 만들어서 단위테스트 가능)
...
}
- 의존성 주입 받는 경우 가짜 객체(@Mock)를 만들어서 의존성 주입이 가능하다.
ApplicationContext
-BeanFactory
-메시지 소스 처리 기능 (i18n)
-이벤트 발행 기능
-리소스 로딩 기능
-기타 등등..
어플리케이션 컨텍스트에서 실질적으로 가장 많이 사용하는 것은 빈팩토리이다. (빈팩토리 상속)
- 추가적으로 이벤트 퍼블리셔, 인바이러먼트, 메세지 소스 등 다른 인터페이스를 구현하고 활용한다.
즉, 어플리케이션 컨텍스트는 다앙햔 기능을 추가로 더 가지고 있는 빈팩토리.
IoC 컨테이너 2부 : ApplicationContext와 다양한 빈 설정 방법
스프링 IoC 컨테이너의 역할
-빈 인스턴스 생성
-의존 관계 설정
-빈 제공
-AppcliationContext
1.ClassPathXmlApplicationContext (XML)
2.AnnotationConfigApplicationContext (Java)
-빈 설정
빈 명세서
빈에 대한 정의
이름, 클래스, 스코프, 생성자(constructor), (setter) ..
빈 설정 방법(xml)
application.xml 생성
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<!-- 고전적인 스프링의 빈 설정 방식 -->
<bean id ="bookService"
class="me.whiteeship.springapplicationcontext.BookService">
<property name="bookRepository" ref="bookRepository" /> <!-- name -> setter , ref -> bean id -->
</bean>
<bean id="bookRepository"
class="me.whiteeship.springapplicationcontext.BookRepository"/>
</beans>
빈 설정 방법(java)
xml 기반보다는 java 기반 설정 방법이 굉장히 유연해짐.
ApplicationConfig 생성
@Configuration
public class ApplicationConfig {
@Bean
public BookRepository bookRepository(){
return new BookRepository();
}
@Bean
public BookService bookService(){
BookService bookService = new BookService();
bookService.setBookRepository(bookRepository()); // 의존성 주입을 직접 해 줄수 있다. 유연해짐
return bookService;
// return new BookService(); // (setter 활용시)북서비스에서 Autowired 로 주입받는다면 내가 직접 의존성 주입하지 않아도 잘 동작한다.
}
}
컴포넌트 스캔
- 설정 방법
XML 설정에서는 context:component-scan
자바 설정에서 @ComponentScan - 특정 패키지 이하의 모든 클래스 중에 @Component 애노테이션을 사용한 클래스를 빈으로 자동으로 등록 해 줌.
컴포넌트 스캔 설정 방법(xml)
<!-- 컴포넌트 스캔 방식(xml) -->
<context:component-scan base-package="me.whiteeship.springapplicationcontext"/>
컴포넌트 스캔 설정 방법(java)
스프링 2.5부터 어노테이션 스캔이 활용되었다.
@Configuration
@ComponentScan(basePackageClasses = DemoApplication.class) // 이 클래스가 위치한 곳부터 컴포넌트 스캐닝 시작
public class ApplicationConfig {
}
-> 지금 쓰고있는 스프링에 가장 가까운 방법이다.
추가적으로
@SpringBootApplication 안에 사실 다 들어있다.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}
빈을 주입받는 방법은 이전 포스트 참조.