[Spring][IoC] Spring IoC Container & Bean

2022. 7. 28. 16:18JAVA/Spring

1. Spring IoC(Inversion of Control)

IoC(Inversion of Control)은 제어의 역전이라는 의미로써 어떤 객체의 필드 멤버(참조 변수)에 직접 인스턴스를 생성하지 않고 생성자와 같은 방법을 통해서 주입받아 사용하는 방법을 의미합니다.

 

제어권을 갖고 있는 경우

class OwnerController{
	private OwnerRepository repository = new OwnerRepository();
}

위 코드를 보면 OwnerController 클래스가 필드멤버인 OwnerRepository 인스턴스를 직접생성하는 것을 볼 수 있습니다. 이 상태는 OwnerController 클래스가 OwnerRepository 인스턴스에 대해서 제어권을 직접 갖는 것을 알 수 있습니다.

 

제어권이 역전되는 경우

제어권이 역전이 되는 경우는 OwnerController 클래스가 필드멤버인 OwnerRepository 인스턴스를 직접 생성하지 않고 생성자로부터 받는다면 제어권이 역전이 됩니다.

class OwnerController{
	private OwnerRepository repo;

	public OwnerController(OwnerRepository repo){
		this.repo = repo;
	}

  // repo를 사용합니다.
}

class OwnerControllerTest{
    @Test
    public void create(){
         OwnerRepository repo = new OwnerRepository();
         OwnerController controller = new OwnerController(repo);
    }
}

위와 같이 정의하면 더이상 OwnerController는 의존성을 만드는 일을 하지 않게 됩니다. 의존성을 관리하는 일은 누군가 밖에서 해주는 것입니다. 그래서 제어권이 역전이 되었다고 볼 수 있습니다.

 

2. Spring IoC Container

IoC Container란 무엇인가?

객체를 생성하고 생명주기를 관리하고 의존성을 관리해주는 컨테이너를 의미합니다. 인스턴스의 생성부터 소멸까지의 인스턴스 생명주기 관리를 개발자가 아닌 컨테이너가 대신 해줍니다.

  • IoC 컨테이너는 객체의 생성을 책임지고, 의존성을 관리합니다.
  • POJO의 생성, 초기화, 서비스, 소멸에 대한 권한을 가집니다.
  • 개발자들이 직접 POJO를 생성할 수 있지만 컨테이너에게 맡깁니다.
  • 개발자는 비즈니스 로직에 집중할 수 있습니다.
  • 객체 생성 코드가 없으므로 TDD가 용이합니다.
POJO(Plain Old Java Object)란 무엇인가?
주로 특정 자바 모델이나 기능, 프레임워크를 따르지 않는 Java Object를 지칭합니다.
Java Bean 객체가 대표적입니다.

 

스프링 컨테이너(IoC 컨테이너)의 종류

스프링 컨테이너가 관리하는 객체를 빈(Bean)이라 정의합니다. 이 빈들을 관리한다는 의미로 스프링 컨테이너를 빈 팩토리(BeanFactory)라고 부릅니다.

  • BeanFactory : 객체의 생성과 객체 사이의 수행 관계를 DI(Dependency Injection) 관점에 볼때의 컨테이너
  • ApplicationContext : BeanFactory 인터페이스에 여러가지 컨테이너 기능을 추가한 인터페이스

 

BeanFactory

  • BeanFactory 인터페이스를 구현한 클래스는 단순히 컨테이너에서 객체를 생성하고 DI 처리하는 기능만을 제공합니다.
  • Bean을 생명 주기(등록, 생성, 조회, 반환)를 관리합니다.
  • 빈을 생성하고 분배하는 책임을 지는 클래스
  • getBean() 메서드를 호출하여 특정한 Bean을 조회할 수 있습니다.
  • 보통 BeanFactory를 바로 사용하지 않고 ApplicationContext를 사용합니다. BeanFactory 보다는 ApplicationContext를 사용하는 것이 더 좋습니다.

ApplicationContext

  • BeanFactory와 동일하게 빈들의 생명 주기를 관리합니다.
  • 스프링의 각종 부가 기능을 추가로 제공합니다.
    • 국제화가 지원되는 텍스트 메시지 관리
    • 이미지같은 파일 자원을 로드할 수 있는 포괄적인 방법 제공
    • 리스너로 등록된 빈에게 이벤트 발생을 알려줌

ApplicationContext Bean 조회

다음 예제는 ApplicationContext 필드를 주입받아서 스프링 컨테이너에서 관리하는 OwnerController 빈 객체를 조회하는 예제입니다.

@WebMvcTest(OwnerController.class)
class OwnerControllerTests {

	...

	@Autowired
	ApplicationContext applicationContext;

	@Test
	public void getBean(){
		OwnerController bean = applicationContext.getBean(OwnerController.class);
		assertThat(bean).isNotNull();
	}
}

 

2. Spring Bean

Spring Bean은 IoC 컨테이너가 관리하는 객체를 Bean이라고 정의합니다.

 

예를 들어 OwnerController의 인스턴스를 생성한다고 가정합니다. 그렇다면 다음과 같이 생성할 수 있을 것입니다.

OwnerController ownerController = new OwnerController();

하지만 위와 같은 onwerController 인스턴스는 Bean이 아닙니다.  하지만 다음과 같은 코드로 만들어진 ownerController2는 Bean이 맞습니다.

OwnerController ownerController2 = applicationContext.getBean(OwnerController.class);

ownerController2 인스턴스가 Bean인 이유는 applicationContext 컨테이너(스프링 컨테이너)에서 OwnerController가 Bean으로 등록이 되어있고 getBean 메서드를 통해서 호출하여 반환하여 저장했기 때문입니다.

 

어떻게 스프링 컨테이너 안에 Bean을 등록하는가?

1. Component Scanning

  • OwnerController 클래스 같은 경우 @Controller 애노테이션을 적용하여 Bean으로 등록이 되어있음
  • @Controller 애노테이션안에 @Component 애노테이션이 설정되어 있기 때문에 Bean으로 등록이 가능함
  • 즉, 실질적으로 @Component 애노테이션을 적용한 애노테이션이나 클래스는 Bean으로 등록이 가능함
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

 

애플리케이션에 등록된 ComponentScan 애노테이션은 @Component 애노테이션을 적용한 클래스를 스캔한다음에 스프링 컨테이너에 Bean으로 등록합니다.

@SpringBootApplication
public class PetClinicApplication {
	public static void main(String[] args) {
		SpringApplication.run(PetClinicApplication.class, args);
	}

}


@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}

PetClinicApplication 클래스의 하위 패키지를 전부 스캔하면서 @Controller 또는 @Component 애노테이션이 붙은 클래스를 Bean으로 등록합니다. 

 

@Component 애노테이션 붙은 다른 일부 애노테이션은 다음과 같습니다.

  • @Component
    • @Repository
    • @Service
    • @Controller
    • @Configuration

 

2. 직접 일일히 XML이나 자바 설정 파일에 등록

 

Bean은 어떻게 등록하는가?

자바 설정 파일을 이용해서 빈을 등록할 수 있습니다.

@Configuration
public class SampleConfig {

	@Bean
	public SampleController sampleController(){
		return new SampleController();
	}
}

위와 같이 등록하면 SampleController 클래스에 @Controller 애노테이션을 붙이지 않아도 됩니다.

// @Controller 
// Controller 애노테이션을 붙이지 않아도 스프링 컨테이너에 SampleContrller Bean이 등록되어 있음
public class SampleController {

}

 

References

source code : https://github.com/yonghwankim-dev/spring-petclinic
[Spring] IoC 컨테이너 (Inversion of Control) 란?
[인프런] 예제로 배우는 스프링 입문 (개정판)