제어의 역전(Inversion of Control)으로, 메서드나 객체의 호출작업에 대한 제어권을 개발자가 아닌, 외부에서 결정하도록 역전된 것을 의미한다.
ex) 프레임워크 vs 라이브러리
프레임워크 - 내가 작성한 코드를 프레임워크가 제어하며 대신 실행한다.
라이브러리 - 내가 작성한 코드로 직접 제어의 흐름을 담당한다.
객체의 의존성을 역전시켜 객체간의 결합도를 줄이고, 코드를 유연하게 만든다.
가독성, 코드중복 방지, 유지 보수의 편리가 가능하게 한다.
하지만, 제어 구조가 역전되어 이해하기 어려운 코드가 될 수 있다.
Spring이 제어를 위임하여 모든 의존성 객체를 Spring 실행시점에 만들어주고, 필요한 곳에 주입한다.
스프링이 Bean을 생성하고 관리하는 빈 팩토리(Bean Factory)를 확장한 IoC 컨테이너이다.
스프링 컨테이너는 @Configuration 이 붙은 클래스를 파라미터로 받아서, 설정 정보로 사용한다.
스프링 컨테이너는 객체의 인스턴스를 싱글턴으로 관리하기 때문에 싱글턴 컨테이너라고도 한다.
이로인해, Bean들이 싱글턴 패턴의 특징을 가지게 된다.
스프링 컨테이너(Application Context)가 관리하는 자바 객체를 빈(Bean)이라고 한다.
@Bean 어노테이션을 가지는 메서드는 스프링 컨테이너에 스프링 빈으로 등록된다.
Bean 관련 메서드
* 조회 대상 스프링 빈이 없으면 예외 발생 : NoSuchBeanDefinitionException
Component Scan
@ComponentScan으로 @Component가 붙어있는 모든 클래스의 인스턴스를 생성해 Bean으로 등록한다.
* 컴포넌트 스캔은 탐색위치를 지정할 수 있다.(프로젝트 최상단 권장, SpringBootApplication 내에 포함됨)
excludeFilters로 컴포넌트 스캔에서 제외할 대상 지정가능
Bean 설정파일에 직접 Bean을 등록
스프링 컨테이너가 @Configuration이 붙은 클래스를 설정 정보로 사용하면서 @Bean이 적용된 메서드를 빈으로 등록한다.
스프링 컨테이너는 CGLIB이라는 바이트코드 조작 라이브러리를 사용하여 싱글톤을 보장해준다.
하지만, Bean만 적용하면 CGLIB 기술 없이 순수한 클래스로 스프링 빈에 등록된 것을 확인할 수 있다.
→ 스프링 빈으로 등록은 되지만, 싱글톤을 보장하지 않는다.
Component는 클래스 레벨에서 사용되며,
일반적으로 개발자가 작성한 컨트롤이 가능한 클래스를 Bean으로 등록한다.
Bean은 메서드 레벨에서 사용되며,
개발자가 컨트롤이 불가능한 외부 라이브러리를 Bean으로 등록할 때 Configuration 클래스 내에 사용한다.
스프링이 주로 적용하는 대상이 Java 엔터프라이즈 기술을 사용하는 서버 환경이기 때문이다.
스프링이 처음 설계된 서버 환경이 트래픽이 많은 높은 성능이 요구되는 환경이었고, 하나의 요청을 처리하기 위해 다양한 객체가 계층구조로 이루어져 있어서, 새로운 객체 생성으로 인한 부하를 감당해야 했다.
의존 관계를 주입할 객체를 계속 생성하고 소멸한다면, 아무리 GC가 성능이 좋아졌다고 하더라도 부담이 된다.
그래서 Spring에서는 Bean들을 기본적으로 싱글턴(Singleton)으로 관리한다.
서버는 트래픽이 많은 환경인데 사용자 요청이 많아지게 되면, 요청마다 각 스레드마다 객체를 생성하기 때문에 비용이 매우 크기 때문에 싱글턴으로 관리한다.
디자인 패턴에서 싱글톤(Singleton) 패턴은 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 재사용 하는 형태를 말한다.
Java로 구현한 싱글턴 패턴 문제점
객체의 생성을 스프링에 위임하여 싱글턴 패턴을 적용하지 않아도
스프링 컨테이너가 직접 Spring Bean을 싱글톤으로 관리하면서 기존 싱글턴 패턴의 단점이 사라진다.
스프링 빈은 기본적으로 싱글턴 스코프로 생성되어 스프링 컨테이너의 시작부터 종료까지 유지된다.
싱글턴으로 인해 하나의 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다.
스프링 빈은 내부에 상태정보를 갖지않는 무상태(stateless)로 설계해야 한다!
→ 동시성을 관리하기 위해 synchronized, ThreadLocal 등으로 격리공간을 갖출 수 있는 방법을 고려할 것 같다.