[Spring] 스프링 컨테이너와 스프링 빈

김진성·2022년 9월 19일
0

스프링에는 빈이라는 요소가 존재한다. 이 빈은 스프링 컨테이너에 의해 관리되는 자바 객체로 ApplicationContext.getBean()으로 얻어질 수 있는 객체를 의미한다.

스프링 빈을 생성하는 방법

  1. Component Scan 으로 스프링 빈을 찾기
  2. 빈 설정 파일에 직접 빈을 등록하기

Component Scan

컴포넌트 스캔은 @Componnent를 명시하여 빈을 추가하는 방식으로 클래스 위에 @Component를 붙이면 스프링이 알아서 스프링 컨테이너에 빈을 등록한다.

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

위 코드 상에서 @SpringBootApplication 내부를 보면 @ComponentScan이라는 어노테이션이 존재한다. 이 어노테이션은 @Component로 등록된 인스턴스의 경로를 알려주는 역할을 하고 @Component는 실제로 찾아서 빈으로 등록할 클래스를 의미한다. 이러한 요소들은 Spring 상에서 IoC 컨테이너로 인해 빈으로 등록된다.

그렇다면 어떤 요소를 @Component가 등록하려고 할까??

  1. @Component
  2. @Contoller or @RestController : 스프링 MVC 컨트롤러 꼐층
  3. @Repository : 스프링 데이터 접근 계층
  4. @Service : 비즈니스 로직을 작성하는 계층
  5. @Configuration : 스프링 설정 정보로 인식하여 스프링 빈이 싱글톤을 유지하도록 처리함

Java 코드로 등록하기

자바 코드로 빈을 등록하는 과정에는 2개의 어노테이션만 사용하면 된다. 하나는 @Configuration, 다른 하나는 @Bean 이다.

@Configuration
public class AppConfig {

	@Bean
    public Repository repository() {
    	return new Repository();
    }
    
    @Bean
    public Service service() {
    	return new ServiceImpl(repository());
    }

}

여기서 @Bean은 클래스가 아니라 메소드에 붙여야 한다.

Bean의 속성과 범위

앞서 Bean을 등록하는 방법을 알게 되었다. 구체적으로 Bean에는 어떤 속성들이 있고 어떤 범위가 있는지 알아보자.

Bean 의 속성

속성에 설정된 정보를 토대로 객체가 생성이 된다.

  1. id : Bean의 식별자
  2. class : 자바의 클래스 명
  3. scope : 객체의 범위로 기본은 싱글톤이다.
  4. constructor-arg : 객체 생성 시 생성자에 전달할 인자 (Constructor Injection)
  5. property : 생성시 bean setter에 전달할 인자 (Setter Injection)
  6. init-method : 객체 생성 시 실행되는 함수
  7. destory-method : 객체 소멸 시 호출되는 함수

Bean의 범위

Bean 같은 경우는 메모리 절약을 위하여 기본적으로 싱글톤 객체로 생성이 된다. 따라서 한번 생성이 되면 모든 객체가 동일한 객체를 참조하는 방식을 사용한다. 이외에 다른 범위를 살펴보자.

Bean Scope

  1. Singleton : 하나의 Bean에 대해서 Spring IoC Container 내에 단 하나만 존재
  2. Prototype : 요청마다 새로운 객체를 생성하여 하나의 Bean에 대해서 다수의 객체가 존재
  3. Request : Http Request 하나에 대한 생명 주기 내 단 하나의 객체만 존재
  4. Session : Http Session과 동일한 생명 주기 내 단 하나의 객체만 존재
  5. Global Session : global Http Session의 생명 주기 내 단 하나의 객체만 존재

이렇게 많은 종류의 Bean이 존재하는데 우리는 Bean을 사용함에 있어 적절한 선택을 하는 것이 중요하다.

싱글톤으로 적합한 객체

  1. 상태가 없는 공유 객체 : 상태를 가지고 있지 않으면 동기화 비용이 없기에 매번 이 객체를 참조하는 곳에서는 새로운 객체를 생성할 이유가 없다.

  2. 읽기용으로만 상태를 가진 공유 객체 : 1번과 유사하게 상태를 가지고 있으나 읽기 전용이므로 여전히 동기화 비용이 들지 않는다.

  3. 공유가 필요한 상태를 지닌 공유 객체 : 객체 간의 반드시 공유해야할 상태를 지닌 객체가 하나 있다면, 이 경우에는 해당 상태의 쓰기를 가능한 동기화 할 경우 싱글톤도 적합하다.

  4. 쓰기가 가능한 상태를 지니면서도 사용빈도가 매우 높은 객체 : 애플리케이션 안에서 정말로 사용 빈도가 높다면, 쓰기 접근에 대한 동기화 비용을 감안하고서라도 싱글톤을 고려할만하다.

    1) 장시간에 걸쳐 많은 매우 많은 객체가 생성될 때
    2) 해당 객체가 매우 작은 양의 쓰기 상태를 가지고 있을 때
    3) 객체 생성비용이 매우 클 때

비싱글톤으로 적합한 객체

  1. 쓰기가 가능한 상태를 지닌 객체 : 쓰기가 가능한 상태가 많아서 동기화 비용이 객체 생성 비용보다 크다면 싱글톤으로 적합하지 않다.
  2. 상태가 노출되지 않은 객체 : 일부 제한적인 경우, 내부 상태를 외부에 노출하지 않는 빈을 참조하여 다른 의존 객체와는 독립적으로 작업을 수행하는 의존 객체가 있다면 싱글톤보다 비싱글톤 객체를 사용하는 것이 더 낫다.

싱글톤 빈은 Thread-Safe 한가?

추가적으로 Bean에서 싱글톤을 사용하지만 우리는 동기화도 같이 처리해주는구나 생각할 때가 있다. 그러나 스프링은 싱글톤 레지스트리를 통해 private, static 변수 등의 코드 없이 비즈니스 로직에 집중하고 테스트 코드에 용이한 싱글톤 객체를 제공해주는 것 뿐으로 동기화 문제는 개발자가 처리해야 한다. 만약에 싱글톤 빈이 상태를 가지게 되고 아무런 동기화 처리를 하지 않는다면 멀티스레드 환경에서 부작용이 발생할 수 있다.

@Bean, @Configuration, @Component 비교

@Bean, @Configuration

  • 개발자가 컨트롤이 불가능한 외부 라이브러리들을 Bean으로 등록하고 싶은 경우에 사용된다.
  • 메소드 또는 어노테이션 단위에 붙일 수 있다.
  • 수동으로 스프링 컨테이너에 빈을 등록하는 방법이다 .
  • 유지보수성을 높이기 위해 애플리케이션 전범위적으로 사용되는 클래스나 다형성을 활용하여 여러 구현체를 등록할 때 사용한다.
  • 1개 이상의 @Bean을 제공하는 클래스의 경우 반드시 @Configuration을 명시해주어야 싱글톤이 보장된다.

@Component

  • 개발자가 직접 컨트롤이 가능한 클래스들의 경우에 사용된다.
  • 클래스 또는 인터페이스 단위에 붙일 수 있다.
  • 자동으로 스프링 컨테이너에 빈을 등록하는 방법이다.
  • @Component 하위 어노테이션으로 @Configuration, @Controller, @Service, @Repository 등이 존재한다.
profile
https://medium.com/@jinsung1048 미디엄으로 이전하였습니다.

0개의 댓글