본문 바로가기

Spring/Springboot

스프링 빈 생명 주기(Spring Bean Life Cycle)

기본 개념

  • 빈 (Bean): 스프링이 관리하는 객체
  • 스프링 IoC 컨테이너: Bean(객체)을 모아둔 저장소이자 빈 관리 -> 객체의 생명주기(Life Cycle)인 생성과 소멸 관리, 객체 간 의존성 주입 등

 

@Bean 어노테이션의 주요 내용

  • @Configuration 설정된 클래스의 메소드에서 사용가능
  • 메소드의 리턴 객체가 스프링 빈 객체임을 선언함
  • 빈의 이름은 기본적으로 메소드의 이름
  • @Bean(name="name")으로 이름 변경 가능
  • @Scope를 통해 객체 생성을 조정할 수 있음
  • @Component 어노테이션을 통해 @Configuration 없이도 빈 객체를 생성할 수도 있음
  • 빈 객체에 init(), destroy() 등 라이프사이클 메소드를 추가한 다음 @Bean에서 지정할 수 있음

// 빈 이름 규칙: 클래스의 앞글자만 소문자로 변경

스프링 빈 생명 주기

1. 스프링 IoC 컨테이너 생성

 

2. 빈 등록

 

방법 1) 컴포넌트 스캔과 자동 의존관계 설정: @Component 어노테이션 사용

@Controller, @Service, @Repository는 @Component를 포함하고 있음

@Component
public class ProductService { ... }

 

스프링 서버가 시작될 때 스프링 IoC 에 '빈' 저장

@Component 클래스에 대해서 스프링이 해 주는 일

// 1. ProductService 객체 생성
ProductService productService = new ProductService();

// 2. 스프링 IoC 컨테이너에 빈 (productService) 저장
// productService -> 스프링 IoC 컨테이너

 

 

@Component 적용 조건: @ComponentScan 에 설정해 준 packages 위치와 하위 packages 들

@SpringBootApplication 에 의해 default 설정이 되어 있음

@Configuration
@ComponentScan(basePackages = "com.sparta.springcore")
class BeanConfig { ... }

 

 

방법 2) 객체를 직접 생성하여 빈으로 등록

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 설정 클래스에 @Configuration 어노테이션 추가
public class BeanConfiguration { // 설정 클래스 생성

    @Bean
    public ProductRepository productRepository() {
        String dbUrl = "jdbc:h2:mem:springcoredb";
        String dbId = "sa";
        String dbPassword = "";

        return new ProductRepository(dbUrl, dbId, dbPassword);
    }
}

 

스프링 서버가 시작될 때 스프링 IoC 에 '빈' 저장

// 1. @Bean 설정된 함수 호출
ProductRepository productRepository = beanConfiguration.productRepository();

// 2. 스프링 IoC 컨테이너에 빈 (productRepository) 저장
// productRepository -> 스프링 IoC 컨테이너

 

3. 객체 생성

 

4. 의존관계 주입

  • 생성자 주입 : 객체의 생성과 의존관계 주입이 동시에 일어남
  • Setter, Field 주입 : 객체의 생성 -> 의존관계 주입으로 라이프 사이클이 나누어져 있음

5. 초기화 콜백 메소드 호출

빈이 생성되고 빈의 의존관계 주입이 완료된 후 호출 되는 것 

 

6. 사용

 

방법 1) 필요한 곳에서 @Autowired를 통해 의존성 주입을 받아 사용

 

  • 멤버변수 선언 위에 @Autowired → 스프링에 의해 DI (의존성 주입) 됨
@Component
public class ProductService {
		
    @Autowired
    private ProductRepository productRepository;
		
		// ...
}

 

  • '빈' 을 사용할 함수 위에 @Autowired → 스프링에 의해 DI (의존성 주입) 됨
@Component
public class ProductService {

    private final ProductRepository productRepository;

    @Autowired
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
		
		// ...
}

 

방법 2)  ApplicationContext: 스프링 IoC 컨테이너에서 빈을 수동으로 가져오는 방법

@Component
public class ProductService {
    private final ProductRepository productRepository;

    @Autowired
    public ProductService(ApplicationContext context) { // ApplicationContext: 스프링 IoC컨테이너를 다루기 위한 클래스 
        // 1.'빈' 이름으로 가져오기
        ProductRepository productRepository = (ProductRepository) context.getBean("productRepository");
        // 2.'빈' 클래스 형식으로 가져오기
        // ProductRepository productRepository = context.getBean(ProductRepository.class);
        this.productRepository = productRepository;
    }

		// ...		
}

cf) @Autowired 조건 

  • @Autowired 적용 조건: 스프링 IoC 컨테이너에 의해 관리되는 클래스에서만 가능
  • @Autowired 생략 조건
    • Spring 4.3 버전 부터 @Autowired 생략가능
    • 생성자 선언이 1개 일 때만 생략 가능

 

7. 소멸 전 콜백 메소드 호출

빈이 소멸되기 직전에 호출

 

8. 종료

 

 

 

스프링에서는 지원하는 빈 생명 주기 콜백

 

1. 인터페이스(InitializingBean, DisposableBean) 상속받아 메소드를 오버라이드하여 초기화 콜백 함수 구현

 

public class ExampleBean implements InitializingBean, DisposableBean {
 
    @Override
    public void afterPropertiesSet() throws Exception {  // InitializingBean의 afterPropertiesSet()
        // 초기화 콜백 (의존관계 주입이 끝나면 호출)
    }
 
    @Override
    public void destroy() throws Exception { //DisposableBean의 destroy()
        // 소멸 전 콜백 (메모리 반납, 연결 종료와 같은 과정)
    }
}

단점

  • 스프링 전용 인터페이스인  InitializingBean 인터페이스, DisposableBean 인터페이스를 상속하므로, 스프링 인터페이스에 의존적
  • 메소드를 오버라이드 하기 때문에 이름 변경 불가
  • 코드가 커스터마이징 할 수 없는 외부 라이브러리에 적용 불가

 

2. 설정 정보(Configuration)에 초기화 메서드, 종료 메서드 지정

 

class ExampleBean {
 
    public void init() throws Exception {
        // 초기화 콜백 (의존관계 주입이 끝나면 호출)
    }
 
    public void close() throws Exception {
        // 소멸 전 콜백 (메모리 반납, 연결 종료와 같은 과정)
    }
}
 
@Configuration
class LifeCycleConfig { // 클래스 내부에 초기화/종료 메소드를 구현
    @Bean(initMethod = "init", destroyMethod = "close") // 호출될 메소드 이름 작성
    public ExampleBean exampleBean() {
    	ExampleBean exampleBean = new ExampleBean();
        return exampleBean;
    }
}

 장점

  • 메소드 이름 지정 가능
  • 스프링 빈이 스프링 코드에 의존하지 않음
  • 코드를 고칠 수 없는 외부 라이브러리에 초기화, 종료 메소드를 적용 가능

 

단점

  • Bean 지정시 initMethod와 destoryMethod를 직접 지정해야하는 번거로움

@Bean의 destoryMethod 속성 특징

라이브러리는 대부분 종료 메소드명이 close 혹은 shutdown

@Bean의 destoryMethod는 기본값이 inferred(추론)으로 등록 즉, close, shutdown이라는 이름의 메소드가 종료 메소드라고 추론하고 자동으로 호출해줌 -> 종료 메소드를 따로 부여하지 않아도 됨

추론 기능을 원하지 않는다면 명시적으로 destroyMethod="" 지정 필요

 

 

3. @PostConstruct, @PreDestroy 어노테이션으로 콜백 함수 커스터마이징

 

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class ExampleBean {
 
    @PostConstruct
    public void initialize() throws Exception {
        // 초기화 콜백 (의존관계 주입이 끝나면 호출)
    }
 
    @PreDestroy
    public void close() throws Exception {
        // 소멸 전 콜백 (메모리 반납, 연결 종료와 같은 과정)
    }
}

장점

  • 최신 스프링에서 권장하는 방식
  • 컴포넌트 스캔과 활용도가 높음
  • 스프링 인터페이스에 의존적이지 않음: import문에 패키지가 javax.annotation.xxx 로 스프링에 종속적인 기술이 아닌 JSR-250이라는 자바 표준으로 스프링이 아닌 다른 컨테이너에서도 동작

단점

  • 코드 수정이 불가능한 외부 라이브러리에는 적용할 수 없음