티스토리 뷰

728x90
반응형

지난번 글에서, IoC(Inversion of Control) DI(Dependency Injection)에 의해 구현된다고 했었다.

 

여기서 IoC는 제어 역전이라는 뜻을 가지며, 외부 프레임워크가 개발자의 코드를 호출해 실행된다.

 

DI는 풀어쓰면 의존성 주입이며, 객체 간의 의존관계가 소스코드 외부의 설정에 의해 정해지는 방식이라고 할 수 있다.

 

https://gnidinger.tistory.com/450?category=992842

 

[Spring]Spring Framework, Spring Triangle

Spring Framework, 혹은 Spring은 Java/Kotlin을 기반으로 한 오픈소스 웹 프레임워크이다. 특히나 엔터프라이즈급 애플리케이션 개발에 필요한 기능이 종합적으로 포함되어 있는데, 대한민국 전자정부

gnidinger.tistory.com

 

이번 글에선 스프링 컨테이너와 빈에 관해 알아본다.


스프링 컨테이너(Spring Container)

 

스프링 컨테이너는 의존성 주입을 통해 객체의 생명주기(Life Cycle)를 관리하며,

 

생성된 객체에게 추가 기능을 부여하는 독립적인 스프링 컴포넌트(모듈)이다.

 

여기서 스프링이 관리하는 객체를 특별히 빈(Bean)이라고 부른다.

 

스프링 컨테이너는 크게 아래와 같은 두 종류로 나눌 수 있는데,

 

  • Bean Factory - 스프링 컨테이너의 최상위 인터페이스
  • ApplicationContext

ApplicationContext가 Bean Factory 및 추가 기능을 상속받기 때문에 보통 ApplicationContext를 사용한다.

 

따라서 흔히 스프링 컨테이너라 하면 이 ApplicationContext를 가리킨다.

 

구체적으로 ApplicationContext는 인터페이스로 구현되어 있으며, 그 내용은 아래와 같다.

 

ApplicationContext가 Bean Factory로부터 상속받은 기능과 추가로 상속받은 기능을 나열하면 아래와 같으며,

 

  • @Bean이 붙은 메서드의 명을 스프링 빈의 이름으로 사용해 빈 등록, 생성/조회/반환 관리
  • getBean() 메서드를 통해 빈을 인스턴스화 후 개발자가 필요로 할 때 제공
  • ResourceLoader - 파일, 클래스 패스, 외부 등 리소스를 편리하게 조회
  • EnvironmentCapable - 로컬, 개발, 운영 등 환경변수를 구분해서 처리, 앱 구동 시 필요한 정보 관리
  • MessageSource - 메시지 소스를 활용한 국제화 기능 (한국어, 영어 등)
  • ApplicationEventPublisher : 이벤트를 발행하고 구독하는 모델을 편리하게 지원

위의 볼드 처리된 두 줄이 Bean Factory에서 상속받은 기능이다.

 

이어서 스프링 컨테이너에 객체, 즉 빈을 등록하는 이유는 객체간 의존관계 관리는 스프링에 맡겨 의존성을 낮추고

 

개발자는 다른 핵심 로직에 집중하기 위해서이다.

 

조금 더 구체적으로는 객체가 의존관계를 만들 때 스프링 컨테이너에서 해당하는 빈을 찾고, 그 빈과 의존관계를 만든다.

 

이전 글에서 언급한 바와 같이 규모가 커질수록 강한 결합으로 이어진 객체는 수정 및 유지보수가 힘들기 때문에

 

스프링 컨테이너를 이용해 느슨한 결합으로 의존도를 낮추면서 인터페이스에만 의존하도록 하는 것이다.

 


스프링 컨테이너의 생성

 

스프링 컨테이너는 POJO 클래스 구성 메타데이터(Configuration Metadata)를 이용해 앱을 만든다.

 

여기서 구성 메타데이터란 쉽게 말해 상황에 맞는 도움말과 자동완성을 위한 메타데이터(데이터에 대한 데이터)이다.

 

조금 더 구체적으로 말하자면 빈들을 인스턴스화 하는 방법이나 설정  컨테이너에게 하는 지시가 포함되어 있다.

 

또한 스프링 프레임워크는 애너테이션 기반 컨테이너 구성을 지원하는데,

 

쉽게 말하면 복잡한 xml 파일 대신 애너테이션으로 설정을 하는 방식이라고 보면 된다.

 

이어서 스프링 컨테이너는 넘겨받은 구성 정보(AppConfig.class 등) 클래스를 사용해 @Bean의 메서드를 등록하는데,

 

 생성 방식은 아래와 같다.

// Spring Container 생성
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

짧게 말하자면 스프링 컨테이너의 생성은 ApplicationContext의 구현이다.

 

위 코드로 컨테이너를 생성하면 단순하게 컨테이너의 구성정보를 담은 파일이 만들어진다.

 

여기서 컨테이너는 매개변수로 전달받은 AppConfig.class를 바탕으로 객체를 생성하며,

 

생성 이후의 객체 의존관계를 자동으로 연결해 준다.

 

이어서 애플리케이션 클래스도 컨테이너의 생성 및 초기화 후에 실행 가능한 시스템을 갖게 된다.

 

또한 스프링 빈 조회에서 상속관계가 있을시 getBean() 등으로 부모 타입을 조회하면 자식 타입도 전부 조회되는데,

 

컨테이너를 통한 다양한 빈 검색은 스프링 컨테이너의 장점이기도 하다.

 

계속해서 의존성 주입 전과 후의 코드를 비교해보자.

 

  • 의존성 주입 전 

  • 의존성 주입 & 제어역전 후

이를 요약하면 아래와 같다.

 

  • new를 이용한 직접적인 객체 생성(강한 결합)에서 생성자를 통한 의존성 주입으로 느슨한 결합이 된 것을 볼 수 있다.
  • userRepository와 discountInfo 클래스는 Bean설정에 따라 유연하게 변할 수 있다.
  • 이를 OrderServiceImpl 입장에서 보자면 주입되는 객체의 종류와 내용에 대해 알 필요가 없어진다.
  • 객체의 주입은 외부 클래스(AppConfig.class)에서 정하며 OrderServiceImpl는 실행에만 집중할 수 있다.

 

의존성 주입은 생성자를 통한 것 말고도 몇 가지가 더 있는데, 이는 다른 글에서 정리하기로 한다.


스프링 빈(Spring Bean)

 

스프링이 다양한 형식을 지원하는 건 BeanDefinition이라는 추상화 덕분이다.

 

BeanDefinition은 빈 설정 메타데이터라고도 불리는데, xml, java파일을 이용해서 만들어진다.

 

이때 스프링 컨테이너는 BeanDefinition이 무엇으로부터 만들어진 것인지는 몰라도 되는데,

 

이는 역할과 구현을 분리한 추상화에 해당한다.

 

참고로 BeanDefinition에 저장되는 빈에 대한 메타데이터는 매우 방대한데, 일부만 뽑아오면 아래와 같으며,

 

특성 설명
BeanClassName 생성할 빈의 클래스명
factoryBeanName 팩토리 역할의 빈을 사용할 경우 이름
factoryMethodName 빈을 생성할 팩토리 메서드 지정
Scope 싱글톤(기본값)
Lazyinit 실제 빈을 사용할 때까지 빈 생성을 지연처리 하는지 여부
InitMethodName 빈 생성, 의존관계 적용 뒤 호출되는 초기화 메서드 이름
DestoryMethodName 빈 제거 직전 호출되는 메서드 이름
Constructor arguments, Properties 의존관계 주입시 사용

BeanDefinition을 생성하는 구조를 조금 더 자세히 보면 아래와 같다.

 

이때 자바로 된 설정코드(AppConfig.class)를 전달하는 코드가 위에 쓰였던 코드가 된다.

// Spring Container 생성
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

계속해서 스프링 컨테이너는 BeanDefinition을 레시피 삼아 객체를 생성하고 관리하게 되는데,

 

 스프링 컨테이너에 의해 관리되고 재사용되는 객체를 빈(Bean)이라고 부른다.

 

*참고 - 자바 빈(Java Bean)

 

  • getter / setter 메서드만 가짐
  • 전달 인자가 없는(no-argument) 생성자
  • private 필드만 가짐 - getter / setter를 통해서만 접근 가능
  • 자바 빈 ≠ 스프링 빈
  • 일반적으로 빈이라고 하면 스프링 빈을 가리킴

스프링 컨테이너에 객체 관리를 맡김으로써 동일한 객체를 여러 번 생성한다거나 하는 불편함에서 벗어날 수 있다.


스프링 컨테이너에 빈 등록

 

스프링 컨테이너에 빈을 등록하는 데에는 크게 두 가지 방법이 있다.

 

  1. 컴포넌트 스캔(Component Scan)
  2. 자바 코드로 직접 등록

하나씩 간단하게 알아보자.

 

1. 컴포넌트 스캔(Component Scan)

 

컴포넌트 스캔은 단순하게 @Component 애너테이션을 사용해 빈을 추가하는 방법이다.

 

@Component 외에도 @Controller , @Service, @Repository, @Configuration 등의 애너테이션을 추가할 수 있는데,

 

이 네 가지 애너테이션이 모두 @Component를 상속받고 있기 때문이다.

 

참고로 네 가지 애너테이션의 차이는 아래와 같다.

 

  • @Controller - 스프링 MVC로 인식
  • @Service - 특별한 처리는 하지 않으나, 핵심 비즈니스 계층임을 표시
  • @Repository - 스프링 데이터 접근 계층으로 인식, 발생하는 예외는 전부 DataAccessException으로 처리
  • @Configuration - 스프링 설정 정보로 인식, 빈이 싱글톤을 유지하도록 추가 처리

해당되는 애너테이션을 사용한 클래스는 스프링 컨테이너에 의해 자동으로 빈 등록이 된다.

 

 

2. 자바 코드로 직접 등록

 

AppConfig.class 등의 설정 클래스를 만들어 직접 입력해주는 방식이다.

 

설정 클래스에 위에서 알아본@Configuration 애너테이션을 추가하고, 메서드 위에 @Bean을 붙여주면

 

해당 클래스는 자동으로 빈 설정을 담당하는 클래스가 되며, 해당 메서드의 리턴 객체는 자동으로 빈 등록이 된다.

 

추가로 @Configuration 없이도 @Bean을 사용할 수 있지만 이 경우 @Bean메서드가 빈 간의 의존성을 선언할 수 없다.

 

이를 lite모드라고 하는데, 지금은 @Bean을 사용하려면 반드시 클래스에 @Configuration을 붙여줘야 한다고 기억하자.

 

빈의 이름은 기본적으로 메서드의 이름이 되며, @Bean(name="name")을 이용해 바꿀 수도 있다.

 

끝으로 두 가지 방식의 차이에 대해 살펴보자.

 

  • @Component

    • 자동으로 스프링 컨테이너에 빈을 등록하는 방법
    • 클래스 레벨에서 선언
    • 스프링의 @ComponentScan 기능이 @Component 애너테이션이 달린 클래스를 자동으로 찾아서 빈으로 등록
    • 많은 경우 @Component를 이용한 자동 등록 방식을 사용하는 것이 좋음
    • @Component 하위 애너테이션으로 @Controller, @Service, @Repository, @Configuration 등이 있음

 

 

  • @Configuration, @Bean

    • 수동으로 스프링 컨테이너에 빈을 등록하는 방법
    • @Configuration은 클래스 레벨, @Bean은 메서드 레벨에서 선언
    • 개발자가 직접 제어가 불가능한 라이브러리를 빈으로 등록할 때 불가피하게 사용
    • 유지보수성을 높이기 위해 앱 전범위적으로 사용되는 클래스나 다형성을 활용하여 여러 구현체를 빈으로 등록할 때 사용
    • 1 이상의 @Bean 제공하는 클래스의 경우 반드시 @Configuration 명시해 주어야 싱글톤이 보장됨
반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함