스프링 핵심 기술-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 반환한다.
  }
}
  1. 직접 생성하는 경우 (단위테스트 힘듬) 의존성을 가진 북서비스의 단위 서비스 테스트 만들기가 어렵다.
@Mock
BookReposotitory bookReposotitory;

@Test
public void save(){
  Book book = new Book();
  when(bookRepository.save(book)).thenReturn(book); // null 일경우 동일한 book 인스턴스 return 
  // (북서비스에서 변경된 북 - 가짜 목업 객체 만들어서 단위테스트 가능)
  ...
}
  1. 의존성 주입 받는 경우 가짜 객체(@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 {
    ...
}

빈을 주입받는 방법은 이전 포스트 참조.




© 2019. by jaeuk