on
IoC 컨테이너 1부
스프링 프레임워크 핵심기술을 공부하고 정리하는 포스트입니다.
IoC 컨테이너
IoC란?
Inversion of Control의 약자로 의존 관계 주입(Dependency Injection)이라고도 하며, 어떠한 객체가 사용하는 의존 객체를 직접 만들어 사용하는 것이 아니라, 주입 받아 사용하는 방법을 말한다.
▶ BookService.java
package me.gracenam.demospringioc.book;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Date;
@Service
public class BookService {
private BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public Book save(Book book) {
book.setCreated(new Date());
book.setBookStatus(BookStatus.DRAFT);
return bookRepository.save(book);
}
}
위 코드를 예시로 살펴보자.
BookService
라는 타입의 객체가 사용할 BookRepository
라는 타입의 의존 객체는 보통 직접 만들어서 사용한다.
@Service
public class BookService {
private BookRepository bookRepository = new BookRepository();
...
}
하지만 IoC, 의존 관계 주입은 직접 생성하는 것이 아닌 어떤 장치(위 코드에서는 생성자)를 사용해서 주입을 받아서 사용한다.
@Service
public class BookService {
private BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
...
}
물론 IoC는 스프링이 없어도 저러한 장치가 마련된다면 직접 할 수도 있다. 가령 BookService
를 만들어 테스트를 할 때, 객체(여기선 BookRepository
)를 원하는데로 만들어서 사용할 수 있다.
public class BookServiceTest {
@Test
public void save() {
BookRepository bookRepository = new BookRepository();
BookService bookService = new BookService(bookRepository);
}
}
그렇다면 왜 IoC 컨테이너를 사용하는 것일까?
그 이유는 여러 개발자들이 커뮤니티에서 논의하여 만들어낸 다양한 Dependency Injection 방법과 Best Practice, Know-How가 쌓여있는 프레임워크이기 때문이다.
IoC 기능을 제공하는 빈을 담고 있기 때문에 컨테이너라고 불리며 빈(Bean, 컨테이너 내부에 들어있는 객체)과 컨테이너에 대한 설계를 할 때 아주 많은 개발자들의 의견을 바탕으로 만들어졌다. 초기에는 Xml이 대세였지만, 개발자들의 의견을 수렴하여 구글 주스(Google Gucie)가 처음 선보인 Annotation기반의 Dependency Injection을 지원하기 시작했다.
현재는 @Service
, @Repository
, @Autowired
와 같은 Annotation을 사용해 객체를 빈으로 등록할 수도 있고, 빈에 등록이 되어있는 객체를 손쉽게 주입받아 사용할 수 있다.
BeanFactory
스프링 IoC 컨테이너의 가장 최상위에 있는 인터페이스는 BeanFactory
라는 인터페이스이다.
내부에 다양한 인터페이스가 있는데, BeanFactory
인터페이스가 가장 핵심이다. IoC 컨테이너의 가장 핵심클래스로 공식문서를 살펴보면 다양한 BeanFactory
라이프사이클 인터페이스의 순서와 이름이 나와있다.
- BeanNameAware’s setBeanName
- BeanClassLoaderAware’s setBeanClassLoader
- BeanFactoryAware’s setBeanFactory
- EnvironmentAware’s setEnvironment
- EmbeddedValueResolverAware’s setEmbeddedValueResolver
- ResourceLoaderAware’s setResourceLoader (only applicable when running in an application context)
- ApplicationEventPublisherAware’s setApplicationEventPublisher (only applicable when running in an application context)
- MessageSourceAware’s setMessageSource (only applicable when running in an application context)
- ApplicationContextAware’s setApplicationContext (only applicable when running in an application context)
- ServletContextAware’s setServletContext (only applicable when running in a web application context)
- postProcessBeforeInitialization methods of BeanPostProcessors
- InitializingBean’s afterPropertiesSet
- a custom init-method definition
- postProcessAfterInitialization methods of BeanPostProcessors
이러한 다양한 라이프사이클을 통해서 스프링이 여러가지 기능을 제공할 수 있는 것이다.
Bean
스프링 IoC 컨테이너에 들어가 있는 객체, 즉 IoC 컨테이너가 관리하는 객체를 빈(Bean)이라고 한다.
위에 사용했던 코드들을 다시 살펴보자.
package me.gracenam.demospringioc.book;
import java.util.Date;
public class Book {
private Date created;
private BookStatus bookStatus;
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public BookStatus getBookStatus() {
return bookStatus;
}
public void setBookStatus(BookStatus bookStatus) {
this.bookStatus = bookStatus;
}
}
Book
클래스는 빈일까 아닐끼? 정답은 아니다. Book
은 스프링 IoC 컨테이너가 관리하지 않기 때문에 스프링 빈이 아니다. 굳이 따지자면 Getter Setter가 있으므로 자바 빈 스펙을 준수하기 때문에 자바 빈(자바 객체)라고 할 수 있겠다.
package me.gracenam.demospringioc.book;
import org.springframework.stereotype.Repository;
@Repository
public class BookRepository {
public Book save(Book book) {
return null;
}
}
BookRepository
는 빈이 맞다. @Repository
라는 애노테이션이 붙었기 때문에 AutoScan을 통해 빈으로 등록이 된다.
package me.gracenam.demospringioc.book;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Date;
@Service
public class BookService {
private BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public Book save(Book book) {
book.setCreated(new Date());
book.setBookStatus(BookStatus.DRAFT);
return bookRepository.save(book);
}
}
BookService
역시 빈이다. @Service
애노테이션을 사용했기 때문에 빈으로 등록이 된다.
package me.gracenam.demospringioc.book;
public enum BookStatus {
DRAFT, PUBLISHED;
}
마지막으로 BookStatus
는 빈이 아니다. 스프링 IoC 컨테이너에서 관리할 필요가 없는 객체들이다.
왜 BookRepository와 BookService는 빈으로 등록이 되었는가?
첫 번째 이유는 의존성 주입때문이다. 의존성 주입을 받기 위해서는 빈으로 등록이 되어 있어야 한다.
두 번째는 빈의 Scope때문이다. 이 Application 전체에서 BookService
라는 인스턴스는 오직 하나만 사용되면 된다. 굳이 여러 개를 만들어 사용할 필요가 없다. 이렇게 하나만 사용하는 것을 싱글톤(Singleton)이라고 하며 싱글톤으로 객체를 만들어 관리하고 싶을 때 IoC 컨테이너를 사용하면 편리하다.
빈의 Scope에는 싱글톤과 프로토타입이 있는데, 스프링 IoC 컨테이너에 등록되는 빈들은 기본적으로 싱글톤으로 등록이 된다. 싱글톤은 해당 빈의 인스턴스를 오직 하나만 생성해 모든 Application에 사용하는 것을 말하며 프로토타입(Prototype)은 매번 다른 객체를 생성하는 것이다.
싱글톤으로 사용이 된다면 Application 전반에서 같은 객체를 사용하기 때문에 효율적이고 런타임 시 성능 최적화에도 유리하다.
마지막으로 라이프사이클 인터페이스가 지원이 되기 떄문이다. 어떤 빈이 만들어졌을 때 추가적인 작업을 원하는 경우가 있다.
package me.gracenam.demospringioc.book;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Date;
@Service
public class BookService {
private BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public Book save(Book book) {
book.setCreated(new Date());
book.setBookStatus(BookStatus.DRAFT);
return bookRepository.save(book);
}
@PostConstruct
public void postConstruct() {
System.out.println("=====================");
System.out.println("Hello Spring");
}
}
빈이 만들어질 때 라이프사이클 콜백에 해당하는 @PostConstruct
애노테이션이 붙어있는 메서드가 호출되도록 했다. 이처럼 다양한 인터페이스를 활용해서 부가적인 기능들을 만들어 낼 수 있다.
ApplicationContext
BeanFactory
와 함께 IoC 컨테이너에서 중요한 또 다른 인터페이스가 있다. 바로 ApplicationContext
로 실질적으로 가장 많이 사용하게 될 BeanFactory
이다.
ApplicationContext
도 결국 BeanFactory
를 상속받았고 IoC 컨테이너로서의 기능을 가지고 있으면서도 추가적인 기능을 가지고 있다.
BeanFactory
에 비해 다양한 기능을 가지고 있는 인터페이스라고 생각하면 된다.
Reference
Comments
SPRING 의 다른 글
-
스프링 타입 컨터버 24 Jun 2022
-
API 예외 처리 17 Jun 2022
-
예외 처리와 오류 페이지 12 Jun 2022
-
로그인 처리 - 인터셉터 08 Jun 2022
-
로그인 처리 - 필터 06 Jun 2022
-
로그인 처리 - 쿠키, 세션 31 May 2022
-
Bean Validation 22 May 2022
-
검증 22 May 2022
-
메시지, 국제화 21 May 2022
-
타임리프 - 스프링 통합과 폼 19 May 2022
-
타임리프 - 기본 기능 10 May 2022
-
스프링 MVC 기본 기능 - 웹 페이지 만들기 02 May 2022
-
스프링 MVC 기본 기능 - HTTP 응답 30 Apr 2022
-
스프링 MVC 기본 기능 - HTTP 요청 24 Apr 2022
-
스프링 MVC 기본 기능 - 요청 매핑 19 Apr 2022
-
스프링 MVC 기본 기능 19 Apr 2022
-
스프링 MVC 구조 이해 14 Apr 2022
-
MVC 프레임워크 만들기 - V4, V5 12 Apr 2022
-
MVC 프레임워크 만들기 - V1, V2, V3 09 Apr 2022
-
서블릿, JSP, MVC 패턴 05 Apr 2022
-
서블릿 29 Mar 2022
-
웹 애플리케이션 이해 24 Mar 2022
-
스프링 웹 계층이란? 05 Nov 2021
-
스프링 시큐리티 공식문서 번역 27 Sep 2021
-
스프링 AOP 총정리 : 개념, 프록시 기반 AOP, @AOP 27 Apr 2021
-
SpEL (스프링 Expression Language) 25 Apr 2021
-
데이터 바인딩 추상화 : Converter와 Formatter 21 Apr 2021
-
데이터 바인딩 추상화 : PropertyEditor 12 Apr 2021
-
Validation 추상화 10 Apr 2021
-
Resource 추상화 08 Apr 2021
-
IoC 컨테이너 9부 07 Apr 2021
-
IoC 컨테이너 8부 06 Apr 2021
-
IoC 컨테이너 7부 02 Apr 2021
-
IoC 컨테이너 6부 29 Mar 2021
-
IoC 컨테이너 5부 27 Mar 2021
-
IoC 컨테이너 4부 23 Mar 2021
-
IoC 컨테이너 3부 20 Mar 2021
-
IoC 컨테이너 2부 18 Mar 2021
-
IoC 컨테이너 1부 12 Mar 2021
-
스프링 PSA 07 Jan 2021
-
스프링 @AOP 실습 07 Jan 2021
-
프록시 패턴 06 Jan 2021
-
스프링 AOP 04 Jan 2021
-
의존성 주입(Dependency Injection) 04 Jan 2021
-
스프링 빈(Bean) 02 Jan 2021
-
스프링 IoC 컨테이너 01 Jan 2021
-
스프링 IoC 01 Jan 2021