스프링 생명주기와 @Bean

스프링에서 빈을 최초에 생성하는 작업을 크게 본다면 '생성'이라고 할 수 있겠지만,

상세히 구분한다면 생성 초기화 작업으로 나눌 수 있다.

 

생성자를 통해서 내부 값들을 설정하는 가벼운 작업의 경우에 생성자에서 처리하는 것이 좋겠지만, 초기화 작업이 무거워지는 경우도 존재한다. 예를 들면.. 데이터베이스 커넥션 풀 관련해서 애플리케이션 시작 시점에 모든 연결을 하고, 종료 시점에 모든 연결을 종료하는 작업이 있다. 이런 무거운 작업의 경우에는 객체 생성과 초기화에 대한 부분을 완전히 분리하는 것이 좋을 때도 있다. 

 

객체의 초기화와 종료를 위한 별도의 작업이 필요한 경우에 콜백 메서드를 사용할 수 있다.

스프링은 의존 관계 주입이 완료되면, 초기화 콜백 메서드를 통해서 초기화 작업을 수행하고,

스프링 컨테이너가 종료되기 전에 소멸 콜백 메서드를 통해서 종료 작업을 수행한다. 

이 두 가지를 묶어서 빈 생명주기 콜백이라고 부른다. 

https://engineerinsight.tistory.com/111

 

@PostConstruct

  • @PostConstruct를 이용하면 스프링이 해당 빈의 생성자와 의존성 주입을 끝낸 뒤, 초기화 시점에 호출해준다.
  • JSR 250에 명시된 스펙으로서 스프링 프레임워크에 의존적이지 않다
  • 초기화에 사용할 메서드 선언부 위에 @PostConstruct 어노테이션을 붙여주면 된다.
  • spring boot 3.x.x 부터는 jakarta.annotation.PostConstruct를 사용해야한다
@Component
class MyCustomInterceptor : HandlerInterceptor {

    @PostConstruct
    fun initialize() {
        AClass.getInstance()  // 이 시점이면 대부분의 빈이 초기화 완료됨
    }
}

 

 

  호출 시점
생성자 빈 생성 직후
init {} 블록 빈 생성 직후
@Bean(initMethod="init") 스프링이 해당 메서드 호출 시
@PostConstruct 의존성 주입 완료 후

Spring Bean 생명주기와 호출 시점 정리

순서  호출 시점  설명  관련 방식
1️⃣ 생성자 호출 Spring이 빈 객체를 생성함 (메모리 할당) 생성자, init {}
2️⃣ 의존성 주입 완료 다른 빈 주입이 완료됨 @Autowired, 생성자 주입
3️⃣ 초기화 콜백 호출 의존성 주입까지 완료된 시점에서 스프링 초기화 작업을 수행 @PostConstruct, afterPropertiesSet()

4️⃣ 커스텀 초기화 메서드 호출 @Bean(initMethod = "...")로 등록한 메서드 호출 initMethod="init"
5️⃣ 빈 사용 가능 상태 DI가 완료된 객체를 스프링이 준비시켜 사용함 -
6️⃣ 컨텍스트 종료 시 콜백 호출 빈이 파괴되기 전 정리 작업 @PreDestroy, destroyMethod, DisposableBean.destroy() 등

 

  • 스프링 프레임워크에서는 빈을 생성한 후에 초기화할 수 있는 여러가지 방법을 제공
  • 스프링 빈(Bean)이 생성되고 의존성 주입(Dependency Injection, 이하 DI)까지 완료된 후에 실행되는 초기화(initialization) 메서드

초기화 콜백 호출 시점에 브레이크포인트를 걸어 확인하기

@Component
class MyCustomInterceptor : HandlerInterceptor {

    @PostConstruct
    fun initialize() {
        AClass.getInstance()  // 여기에  breakpoint를 설정해준다
    }
}

 

application.yml 또는 logback.xml 에서 Spring 내부 로그를 DEBUG로 바꾸면 전체 Bean Lifecycle(
초기화 순서)를  로그로 볼 수 있음

Bean으로 관리하기

  • @Component, @Service, @Bean 등으로 빈으로 등록되지 않으면 @PostConstruct는 호출되지 않는다.
  • Spring은 @Bean으로 등록된 메서드를 호출할 때 프록시를 써서 싱글턴 관리한다.

 

잘못된 생성 예

@Configuration
class WebMvcConfig : WebMvcConfigurer {

    override fun addInterceptors(registry: InterceptorRegistry) {
        registry.addInterceptor(MyCustomInterceptor()) // XXX new로 직접 생성 = 스프링 빈 아님
    }
}

 

바른 예

@Bean
fun myCustomInterceptor(): MyCustomInterceptor {
    return MyCustomInterceptor() // 스프링이 이걸 빈으로 관리함
}

 

@Bean 어노테이션과 XML

@Bean

@Configuration
class AppConfig {

	@Bean
	fun transferService() = TransferServiceImpl()
}

 

XML

<beans>
	<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

 

Using the @Bean Annotation

https://docs.spring.io/spring-framework/reference/core/beans/java/bean-annotation.html#beans-java-lifecycle-callbacks

  • You can use the @Bean annotation in a @Configuration-annotated or in a @Component-annotated class.
  • To declare a bean, you can annotate a method with the @Bean annotation.
  • By default, the bean name is the same as the method name.
  • Any classes defined with the @Bean annotation support the regular lifecycle callbacks and can use the @PostConstruct and @PreDestroy annotations from JSR-250. (https://docs.spring.io/spring-framework/reference/core/beans/annotation-config/postconstruct-and-predestroy-annotations.html)
  • If you have a public close or shutdown method and you do not wish for it to be called when the container shuts down, you can add @Bean(destroyMethod = "") to your bean definition to disable the default (inferred) mode.

Specifying Bean Scope

  • The default scope is singleton, but you can override this with the @Scope annotation

@Component와 @Configuration

@Component

  • @Component 어노테이션이 붙은 클래스는 스프링 컨테이너에 의해 자동으로 빈으로 등록된다

@Configuration

  • 스프링 빈 설정을 위한 클래스를 정의
  • 주로 @Bean 어노테이션과 함께 사용
  • 외부 라이브러리나 복잡한 설정 로직을 가진 빈을 등록할 때 사용
  • 데이터베이스 연결 설정, 외부 API 클라이언트 설정 등 복잡한 설정 로직을 가진 빈을 등록할 때
  • @Configuration 어노테이션이 붙은 클래스는 빈 설정 정보를 담는 클래스로 인식되며, @Bean 어노테이션이 붙은 메서드들을 통해 빈을 생성하고 관리

@Component는 주로 클래스 자체를 빈으로 등록하는 반면,

@Configuration은 @Bean 메서드를 통해 빈을 생성하고 등록한다.

 


코틀린에서의 클래스와 메서드는 기본적으로 final 이다.

만약 open 키워드를 사용하지 않을 경우 다른 곳에서 상속 받지 못하는 final class로 정의된다.
 어떤 클래스의 상속을 허용하려면 해당 클래스 앞에 open 변경자를 붙여야 하며 오버라이드를 허용하고 싶은 메서드나 프로퍼티의 앞에도 open 변경자를 붙여야 한다.

 

java synchronized 

현재 데이터를 사용하고 있는 해당 스레드를 제외하고 나머지 스레드들은 데이터에 접근 할 수 없도록 막는 개념