스프링 Di, Bean등록에 대해 알아보자

섭정이·2025년 4월 10일
post-thumbnail

Spring 컨테이너와 Bean 등록 정리

기존에 서블릿을 통해 프론트컨틀로러 핸들러어뎁터 핸들러 맵핑을 구현하고 보드게시판을 만들어 보았다. 하지만 직접 하려니 조금 불안정한부분도 있었고 하여 해당 방식을 미리 만들어놓은 스프링 프레임워크를 공부하여 적용해 보기로 하였다. 우선은 추상적인 개념 보다는 사용방식부터 알아보았다.

mvc 부분은 다음에 보기로 하고 일단은 스프링의 핵심인 컨테이너, 빈부터 고웁해 보기로 하였다.

1. Bean 등록 방식

빈등록은 수동등록과 자동등록으로 나뉜다.

1) 수동 등록

@Configuration
public class AppConfig {
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl();
    }
}
  • 어노테이션 기반 DI(@Autowired) 사용 시 별도 설정 필요
  • xml 기반 등록도 가능 (<bean id="..." class="..."/>)
    @Configuration 을 통해 설정파일을 만들고
    직접 @Bean을 통해 빈 등록을 해준다.

2) 자동 등록

@Component
public class MemberServiceImpl implements MemberService {}
@Configuration
@ComponentScan(basePackages = "com.example")
public class AutoAppConfig {}
  • 자동 등록 시 @ComponentScan 설정 필수
  • 대상 클래스에 @Component, @Service, @Repository, @Controller 등 사용

@ComponentScan 을 통해 파일들을 스캔하고 @Component가 붙은 클래스를 확인 하고 등록한다.


2. 의존성 주입 (DI)

주입방법또한 자동방식과 수동방식으로 나뉜다.

1) @Autowired

@Autowired
private MemberService memberService;
  • 조건: 주입 대상도 Spring Bean으로 등록되어 있어야 함

주입할 대상에 @AutoWired 로 판별하여 주입한다.


2) AnnotationConfigApplicationContext

ApplicationContext 하위의 AnnotationConfigApplicationContext 를 통해 빈을 꺼낼 수 있다.

3. 동일 타입의 Bean이 여러 개일 경우

1) @Qualifier

@Autowired
@Qualifier("mainMemberService")
private MemberService memberService;

2) @Primary

@Primary
@Component
public class MainMemberService implements MemberService {}

인터페이스에 해당하는 등록된 클래스가 두개 이상일때는 primary와 qualifier를 통해 명시해야 한다.

4. @Component 계층 정리

어노테이션역할내부 구성
@Component모든 Bean의 기본최상위
@Repository영속성 계층에 특화예외 변환 지원
@Service서비스 계층 명시 목적특별 기능 없음 (가독성↑)
@ControllerMVC 컨트롤러 등록DispatcherServlet이 인식

5. 스프링 컨테이너 작동 방식

사용방법은 알아보았으니 실제로 어떤방식으로 동작하는지 궁금하여 찾아보았다.

스프링 doc을 참고하였다.

Spring 컨테이너는 ApplicationContext 인터페이스의 구현체를 통해 동작한다.

  • Your Business Objects (POJOs)
    • POJO: Plain Old Java Object
    • 우리가 작성한 순수 자바 클래스들이다. (예: MemberService, OrderRepository 등)
    • 이 객체들은 Spring이 관리하게 될 대상이다.
  • Configuration Metadata (설정 메타데이터)
    • Spring에게 어떤 객체를 관리해야 할지, 어떻게 의존성을 주입해야 할지를 알려주는 설정 정보이다.
    • 설정 방식 예시:
    • 자바 기반: @Configuration + @Bean
    • 어노테이션 기반: @Component, @Service, @Repository
    • XML 설정: 태그 사용
  • The Spring Container
    • 핵심 엔진 역할을 하는 부분이다.
    • ApplicationContext가 대표적인 구현체이며, 내부적으로 BeanFactory를 사용한다.
    • 주요 책임:
    • 설정 메타데이터를 바탕으로 POJO 인스턴스를 생성
    • 의존성 주입(DI)
    • 생명주기 관리 (초기화, 소멸 등)
    • 후처리 (BeanPostProcessor, AOP 등)
  • Fully Configured System – Ready for Use
    • 위 과정을 거쳐 의존성 주입까지 완료된 객체들이 준비된 상태이다.
    • 우리가 실제로 사용할 수 있는 객체들은 Spring 컨테이너가 관리하는 Bean들이다.
    • 예: memberService.join(member)를 호출할 수 있는 상태

컨테이너 초기화 흐름

1. 설정 정보 로딩 (자바 설정 클래스, XML 등)
2. 클래스패스 스캔 or @Bean 기반 객체 인식
3. BeanDefinition 생성 (메타정보 생성)
4. BeanDefinition 등록 (BeanFactory에 저장)
5. Bean 인스턴스 생성
6. 의존성 주입 (Autowired, Constructor, Setter 등)
7. 초기화 콜백 실행
8. 사용 가능 상태로 컨테이너에 보관

즉 내가 이해한 과정은 다음과 같다.

    1. 개발자가 작성한 자바 설정 클래스(@Configuration) 또는 XML 설정 파일을 읽는다.
      여기서 어떤 Bean을 등록할 지에 대한 메타정보가 로딩된다.

      예: AnnotationConfigApplicationContext(AppConfig.class)
      또는 XML 기반에서는 ClassPathXmlApplicationContext("appContext.xml")

    1. @ComponentScan이 있으면, 지정한 패키지 하위의 클래스들 중 @Component, @Service, @Repository, @Controller가 붙은 클래스들을 자동으로 찾아낸다.
      이때 메모리에 실제 객체를 만들지는 않고 정의된 정보만 수집한다.
    1. 스프링은 인식한 클래스들을 기반으로 BeanDefinition이라는 객체를 생성한다. BeanDefinition은 Bean의 클래스명, scope, 초기화 방식, 의존성 정보 등 다양한 메타정보를 담고 있는 객체이다. 이 과정을 통해 객체 생성 전에 미리 준비된 정의 정보를 만들게 된다.
    1. 생성된 BeanDefinition들은 BeanFactory (또는 DefaultListableBeanFactory)에 등록된다. 이 시점에도 아직 실제 인스턴스는 생성되지 않는다. 즉 컨테이너는 “무엇을 어떻게 생성할지”에 대한 청사진만 먼저 등록해 둔다.
    1. 컨테이너는 등록된 BeanDefinition을 바탕으로 실제 Bean 객체를 생성한다. 여기서 싱글톤 스코프 Bean은 컨테이너 시작 시점에 미리 생성된다.
    1. 생성된 객체의 필드나 생성자, 세터에 대해 DI(Dependency Injection)가 수행된다. 이때 @Autowired, @Inject, 생성자 주입 등 다양한 방법으로 의존 객체가 주입된다. 결국 스프링은 이 과정에서 ApplicationContext 내부에 등록된 Bean들을 자동으로 찾아 연결해 준다.
    1. 의존성 주입이 완료된 후, 초기화 작업을 진행한다. PostConstruct 어노테이션이 붙은 메서드나,InitializingBean.afterPropertiesSet()이 호출된다.
    1. 초기화가 완료된 Bean은 ApplicationContext 내부에 보관되며, 이제 다른 Bean이나 클라이언트가 자유롭게 사용할 수 있다. 이후에는 getBean() 또는 @Autowired 등을 통해 주입받아 사용할 수 있다.

주요 인터페이스

인터페이스역할
BeanFactory가장 기본적인 컨테이너. DI만 지원
ApplicationContextBeanFactory 확장판. AOP, 이벤트, 메시지 처리 등 포함
AnnotationConfigApplicationContext자바 기반 설정을 지원하는 구현체
ClassPathXmlApplicationContextXML 기반 설정을 지원하는 구현체

짧게 정리하자면 설정클래스에 명시한 정보를 토대로 BeanDefinition을 생성하여 Beanfactory에 저장해놓고 해당 빈 스코프 생명주기에 따라 실제 빈을 생성한다. 생성한 빈들은 DI작업이 수행될떄 ApplicationContext 내부에 등록된 빈들을 찾아 연결하고 초기화 작업이 실행된 뒤 후에 자유롭게 사용할 수 있는 상태가 된다.


6. Bean 생명주기 상세 흐름

전체 생명주기 순서

1. 객체 생성 (Constructor)
2. 의존성 주입 (@Autowired)
3. 빈 등록 후 처리 (BeanPostProcessor)
4. 초기화 콜백 (InitializingBean, @PostConstruct)
5. 빈 사용 가능
6. 소멸 콜백 (DisposableBean, @PreDestroy)

주요 생명주기 콜백

구분방법설명
초기화@PostConstruct의존성 주입 완료 후 자동 호출
초기화InitializingBean.afterPropertiesSet()수동 구현 가능
소멸@PreDestroy컨테이너 종료 전 자동 호출
소멸DisposableBean.destroy()수동 구현 가능

BeanPostProcessor (빈 후처리기)

@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // 초기화 전 호출
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        // 초기화 후 호출
        return bean;
    }
}
  • 내부적으로 Spring AOP, @Autowired 처리 등도 이 과정에서 이루어짐

생명주기 핵심 요약

단계설명실행 시점
생성자 호출객체 생성Bean 등록 시
의존성 주입@Autowired, 생성자Bean 생성 후
초기화 콜백@PostConstruct, InitializingBeanDI 후
후처리BeanPostProcessor초기화 전/후
소멸 콜백@PreDestroy, DisposableBean컨테이너 종료 시
profile
우직하게

0개의 댓글