스프링 IoC의 용어 정리
-
빈(bean)
빈 또는 빈 오브젝트는 스프링이 IoC 방식으로 관리하는 오브젝트라는 뜻이다. 관리되는 오브젝트라고 부르기도 한다.
스프링을 사용하는 애플리케이션에서 만들어지는 모든 오브젝트가 다 빈은 아니다.
그중에서 스프링이 직접 생성과 제어를 담당하는 오브젝트만을 빈이라고 부른다. -
빈 팩토리(bean factory)
스프링의 IoC를 담당하는 핵심 컨테이너를 가리킨다. 빈을 등록, 생성, 조회, 돌려주는 등의 부가적인 빈을 관리하는 기능을 담당한다.
보통은 이 빈 팩토리를 바로 사용하지 않고 이를 확장한 애플리케이션 컨텍스트를 이용한다.
BeanFactory라고 붙여쓰면 빈 팩토리가 구현하고 있는 가장 기본적인 인터페이스의 이름이 된다. 이 인터페이스에 getBean()과 같은 메소드가 정의되어 있다. -
애플리케이션 컨텍스트(application context)
빈 팩토리를 확장한 IoC 컨테이너이다. 빈을 등록하고 관리하는 기본적인 기능은 빈 팩토리와 동일하다.
ApplicationContext라고 적으면 애플리케이션 컨텍스트가 구현해야 하는 기본 인터페이스를 가리키는 것이다.
ApplicationContext는 BeanFactory를 상속한다. -
설정정보/설정 메타정보(configuration metadata)
스프링의 설정정보란 애플리케이션 컨텍스트 또는 빈 팩토리가 IoC를 적용하기 위해 사용하는 메타정보를 말한다.
설정정보는 보통 IoC 컨테이너에 의해 관리되는 애플리케이션 오브젝트를 생성하고 구성할 때 사용된다. -
컨테이너 또는 IoC 컨테이너
IoC 방식으로 빈을 관리한다는 의미에서 애플리케이션 컨텍스트나 빈 팩토리를 컨테이너 또는 IoC 컨테이너라고도 한다. -
스프링 프레임워크
스프링 프레임워크는 IoC 컨테이너, 애플리케이션 컨텍스트를 포함해서 스프링이 제공하는 모든 기능을 통틀어 말할 때 주로 사용한다.
그냥 스프링이라고 줄여서 말하기도 한다.
제어권의 이전을 통한 제어관계 역전
일반적으로 main() 메서드와 같이 프로그램이 시작되는 시검에서 다음에 사용할 오브젝트를 결정하고
결정한 오브젝트를 생성, 메소드 호출, 그 오브젝트 메소드에서 사용할 것을 결정 등의 작업이 반복된다.
이런 프로그램 구조에서 각 오브젝트는 프로그램 흐름을 결정하거나 사용할 오브젝트를 구성하는 작업에 능동적으로 참여한다.
제어의 역전이란 이런 제어 흐름의 개념을 거꾸로 뒤집는 것이다.
제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다. 생성하지도 않는다.
또 자신이 어떻게 만들어지고 사용되는지를 알 수 없다. 모든 제어 권한을 다른 대상에게 위임하기 때문이다.
제어의 역전 개념은 이미 폭넓게 적용되어 있다. 서블릿을 생각해보면,
서블릿을 개발해서 서버에 배포할 수는 있지만 그 실행을 개발자가 직접 제어할 수 있는 방법은 없다.
서블릿 안에 main() 메소드가 있어서 직접 실행할 수 있는 것도 아니다.
대신 서블릿에 대한 제어 권한을 가진 컨테이너가 적절한 시점에 서블릿 클래스의 오브젝트를 만들고 그 안의 메소드를 호출한다.
애플리케이션 컨텍스트의 동작방식
@Configuration
이 붙은 클래스는 이 애플리케이션 컨텍스트가 활용하는 IoC 설정정보다.
애플리케이션 컨텍스트는 클래스를 설정정보로 등록해두고 @Bean
이 붙은 메소드의 이름을 가져와 빈 목록을 만들어둔다.
클라이언트가 애플리케이션 컨텍스트의 getBean()을 호출하면 자신의 빈 목록에서 요청한 이름이 있는지 찾고,
있가면 빈을 생성하는 메소드를 호출해서 오브젝트를 생성시킨 후 클라이언트에 돌려준다.
애플리케이션 컨텍스트를 사용할 때의 장점
- 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다.
- 애플리케이션 컨텍스트는 종합 IoC 서비스를 제공해준다.
- 애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다.
싱글톤 레지스트리로서의 애플리케이션 컨텍스트
애플리케이션 컨텍스트는 IoC 컨테이너이자 동시에 싱글톤을 저장하고 관리하는 싱글톤 레지스트리이기도 하다.
스프링은 별다른 설정이 없으면 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다.
왜 스프링은 싱글톤으로 빈을 만들까?
스프링이 주로 적용되는 대상이 자바 엔터프라이즈 기술을 사용하는 서버 환경이기 때문이다.
즉, 사용자가 많다는 말이다. 매번 클라이언트 요청이 올 때마다 각 로직을 담당하는 오브젝트를 새로 만들어서 사용한다고 생각해보자.
아무리 자바의 오브젝트 생성과 가비지 컬렉션이 좋아졌어도 서버가 감당하기 힘들다.
그래서 기본적으로 싱글톤으로 객체의 갯수를 제한한다.
싱글톤 레지스트리
스프링은 서버환경에서 싱글톤이 만들어져서 서비스 오브젝트 방식으로 사용되는 것은 적극 지지!
하지만 자바의 기본적인 싱글톤 패턴은 여러 가지 단점이 있다..
그래서 스프링은 직접 싱글톤 형태의 오브젝트를 마늗ㄹ고 관리하는 기능을 제공한다. 이것을 싱글톤 레지스트리라고 한다.
스프링 컨테이너는 싱글톤 관리 컨테이너이도 하다.
싱글톤 레지스트리 장점
- static 메서드와 private 생성자를 사용해야 하는게 아니라 평범한 자바 클래스를 싱글톤으로 활용하게 해준다.
오브젝트 생성에 관한 모든 권한은 IoC 기능을 제공하는 애플리케이션 컨텍스트에게 있기 때문이다. - 싱글톤 방식으로 사용될 어플리케이션 클래스라도 public 생성자를 가질 수 있다.
싱글톤으로 사용해야 하는 환경이 아니라면 간단히 오브젝트를 생성해서 사용할 수 있다.
따라서 테스트 환경에서 자유롭게 오브젝트를 만들 수 있고, 목 오브젝트로 대체하는 것도 간단하다.
생성자 파라미터를 이용해서 사용할 오브젝트를 넣어주게 할 수도 있다. - 싱글톤 패턴과 달리 스프링이 지지하는 객체지향 설계 방식과 원칙, 디자인 패턴 등을 적용하는 데 제약이 없다.
스프링은 IoC 컨테이너일 뿐만 아니라, 고전적인 싱글톤 패턴을 대신해서 싱글톤을 만들고 관리해주는 싱글톤 레지스트리라는 점을 기억해두자!
싱글톤으로 만들어지기 때문에 주의해야 할 점
- 싱글톤이 멀티스레드 환경에서 서비스 형태의 오브젝트로 사용되는 경우에는 상태정보를 내부에 갖고 있지 않은
무상태 방식(stateless)으로 만들어져야 한다.
서로 값을 덮어쓰고 자신이 저장하지 않은 값을 읽어올 수 있기 때문에 싱글톤은 기본적으로
인스턴스 필드의 값을 변경하고 유지하는 상태유지 방식(stateful)으로 만들지 않는다.이를 지키지 않고 서버에 배포하면 여러 사용자가 동시에 접속했을 때 데이터가 엉망이 될 수 있다. 읽기전용의 값이라면 초기화 시점에서 인스턴스 변수에 저장해두고 공유하는 것은 아무런 문제가 없다.
- 스프링의 싱글톤 빈으로 사용되는 클래스를 만들 때는 바뀌는 정보는 로컬 변수로 정의하거나, 파라미터로 주고받으면서 사용하게 해야 한다.
초기에 설정해서 바뀌지 않는 읽기전용 인스턴스 변수는 문제가 없지만, 매번 새로운 값으로 바뀌는 인스턴스 변수는 심각한 문제가 발생한다.
무상태 방식이란?
객체에 인스턴스 필드가 없으면 stateless 이다.
final 키워드를 사용한 상수는 괜찮다.
다음과 같은 형태이다.
class Stateless {
final String TEST = "Test!"; // immutable
void test() {
System.out.println(TEST);
}
}
스프링 빈의 스코프
스프링이 관리하는 오브젝트, 즉 빈이 생성되고, 존재하고, 적용되는 범위에 대해 알아보자!
스프링에서 이것을 빈의 스코프라고 한다. 스프링 빈의 기본 스코프는 싱글톤이다.
싱글톤 스코프는 컨테이너 내에 한 개의 오브젝트만 만들어져서, 강제로 제거 하지 않는 한 스프링 컨테이너가 존재하는 동안 계속 유지된다.
경우에 따라 싱글톤 외의 스코프를 가질 수 있다.
- 프로토타입 스코프 : 싱글톤 스코프와는 다르게 컨테이너에 빈을 요청할 때마다 매번 새로운 오브젝트를 만든다.
- 리퀘스트 스코프 : 웹을 통해 새로운 HTTP 요청이 생길때마다 생성된다.
프로그래밍에서 의존성이란?
스프링의 IoC(Inversion of Control / 제어의 역전)라고도 하는 DI(Dependency Injection / 의존성 주입)을 알아보기 전에
자바에서의 의존성이 무엇인지도 알아보자.
new Car();
Car 객체 생성자에서 new Tire();
의존성을 단순하게 정의해서 new라고 하자.
new를 실행하는 Car와 Tire사이에서 Car가 Tire에 의존한다.
결론적으로 전체가 부분에 의존한다고 할 수 있다.
의존하는 객체(전체)와 의존되는 객체(부분) 사이에 집합 관계와 구성 관계로 구분할 수도 있다.
일단 전체가 부분에 의존한다는 것과 프로그래밍에서 의존 관계는 new로 표현된다는 것만 기억하자!
집합 관계 : 부분이 전체와 다른 생명 주기를 가질 수 있다. 예) 집 vs. 냉장고
구성 관계 : 부분은 전체와 같은 생명 주기를 갖는다. 예) 사람 vs. 심장
(1) 스프링 없이 의존성 주입하기 1 - 생성자를 통한 의존성 주입
(2) 스프링 없이 의존성 주입하기 2 - 속성을 통한 의존성 주입
(3) 스프링을 통한 의존성 주입 1 - XML 파일 사용
(4) 스프링을 통한 의존성 주입 2 - 스프링 설정 파일(XML)에서 속성 주입
(5) 스프링을 통한 의존성 주입 3 - @Autowired
를 통한 속성 주입
(6) 스프링을 통한 의존성 주입 4 - @Resource
를 통한 속성 주입