스프링 컨테이너와 ApplicationContext란?
Spring에서 객체를 관리하는 핵심은 바로 스프링 컨테이너입니다. 이 컨테이너는 객체(Bean)를 생성하고, 의존성을 주입하며, 생명주기를 관리합니다.
- ApplicationContext는 스프링 컨테이너의 구체적인 구현체이며, BeanFactory를 확장합니다.
- BeanFactory가 최소 기능만 제공하는 반면, ApplicationContext는 다음을 포함한 다양한 기능을 제공합니다
- 대부분의 스프링 애플리케이션에서는 ApplicationContext를 사용합니다.
Bean 생명주기 전체 흐름
Spring에서 Bean의 생명주기는 다음과 같은 순서로 진행됩니다.
- Bean 인스턴스 생성 (생성자 호출)
- 의존성 주입 (필드, 생성자, setter 등)
- BeanNameAware, BeanClassLoaderAware, ApplicationContextAware 등 콜백 method 호출
- @PostConstruct 또는 InitializingBean.afterPropertiesSet() 또는 init-method 호출
- 컨테이너 종료 시 @PreDestroy 또는 DisposableBean.destroy() 또는 destroy-method 호출
API 서버에서의 Bean 생명주기 실제 흐름
구조 예시
Client → [UserController] → [UserService] → [UserRepository]
모든 컴포넌트는 @RestController, @Service, @Repository 등으로 등록되어 있고, Spring이 자동으로 Bean으로 관리합니다.
1️⃣ Bean 인스턴스 생성 (UserService 기준으로 설명)
@Service
public class UserService {
public UserService() {
System.out.println("1. 생성자 호출");
}
}
서버 실행 시, 스프링이 @Service로 등록된 클래스를 탐색하고 new UserService() 호출합니다.
2️⃣ 의존성 주입
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository; // 자동 주입
}
- Spring은 UserRepository도 Bean으로 등록되어 있으므로 이를 UserService에 자동 주입합니다.
- 이 작업은 생성자 또는 필드, 세터 방식으로 수행됩니다.
3️⃣ Aware 인터페이스 (필요 시)
@Component
public class MyLogger implements ApplicationContextAware {
public void setApplicationContext(ApplicationContext applicationContext) {
System.out.println("3. ApplicationContext 주입");
}
}
- 특별한 처리가 필요한 경우, 스프링 컨텍스트 등 내부 객체에 접근하기 위해 Aware 인터페이스를 사용할 수 있습니다.
- 일반 API 개발에서는 자주 쓰이지는 않지만, 로그 추적기, 메타정보 접근 등에 사용됩니다.
4️⃣ 초기화 콜백
- Bean 생성 → 의존성 주입이 완료된 직후 실행됩니다.
- 즉, 해당 Bean이 완전한 상태가 되었을 때 Spring이 자동으로 호출해 줍니다.
- 이 시점에서는 다음과 같은 "초기 설정 작업"을 주로 수행합니다.
목적 | 예시 |
외부 서비스 연결 | DB 연결 확인, Redis 클라이언트 초기화, AWS 인증 |
인증 토큰 획득 | 외부 API 사용을 위한 OAuth 토큰 요청 |
초기 데이터 로딩 | 캐시 미리 로딩, 기본 설정 값 불러오기 |
상태 점검 | 구성 요소가 정상 동작하는지 체크 |
Scheduler 등록 | 일정 주기 작업 예약 |
내부 동작 (Spring은 어떻게 처리하나?)
- Spring이 Bean을 new로 생성
- 필드/생성자 등으로 DI 수행
- @PostConstruct가 붙은 메서드를 리플렉션으로 찾아서 한 번 실행
👉 실무 예시: Redis 캐시 키 미리 로딩, 외부 API 토큰 발급
@Component
@RequiredArgsConstructor
public class CacheWarmUpService {
private final RedisService redisService;
@PostConstruct
public void init() {
System.out.println("캐시 초기화 시작");
redisService.loadAllCategoriesToCache();
}
}
@Component
public class ExternalApiClient {
private String accessToken;
@PostConstruct
public void init() {
this.accessToken = requestTokenFromApi();
System.out.println("API 토큰 발급 완료: " + accessToken);
}
private String requestTokenFromApi() {
// HTTP 요청으로 OAuth 토큰 받기
return "access_token_xyz";
}
}
5️⃣ Bean 사용 (실제 API 요청 처리)
서버 기동이 완료되면, 사용자가 API 요청을 보낼 때 컨트롤러가 호출되고 Bean을 사용합니다.
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
}
이때 userService는 이미 DI + 초기화가 완료된 상태입니다.
6️⃣ 소멸 콜백 (서버 종료 시점)
서버가 종료될 때 등록된 Bean들의 소멸 메서드가 순차적으로 호출됩니다.
@PreDestroy
public void destroy() {
System.out.println("5. @PreDestroy 호출");
// 예: 커넥션 종료, 임시 파일 삭제
}
이건 보통 서버 종료, docker stop , ./gradlew bootRun 중지할 때 트리거됩니다.
끝.
'Study > SpringBoot' 카테고리의 다른 글
Spring MVC와 Spring Security의 예외 처리 차이점 (0) | 2025.05.05 |
---|---|
JPA의 flush()는 언제, 왜 호출될까? (0) | 2025.04.12 |
Spring Boot 테스트 코드: 통합 테스트 vs 단위 테스트 (0) | 2025.03.27 |
SpringBoot + JPA: N+1 문제 발생 원인과 해결 방법 (0) | 2025.03.22 |
Spring Been 객체와 프록시 패턴 (0) | 2025.03.10 |