본문 바로가기

spring

Spring Bean / 생명주기

스프링 컨테이너 : 애플리케이션의 구성 요소를 생성 및 관리

> 개발자가 직접 객체를 생성하고 관리하는 부담을 줄여줌

> 스프링 빈을 생성하고 의존 관계를 주입하는 역할

Bean (스프링 빈) : 스프링 컨테이너가 관리하는 객체
스프링 컨테이너에 의해 생명 주기가 관리됨
제어 역전 (IoC) / 의존성 주입 (DI) 기능

 

Bean의 정의와 역할

 

스프링의 객체 !

Spring IoC 컨테이너에 의해 인스턴스화되고 관리됨

애너테이션 추가 / XML 파일 설정을 통해 Bean이 선언됨

** MyBean 클래스에 @Component 애너테이션을 붙이면 클래스가 빈으로 등록됨

** 빈의 이름은 클래스 이름의 첫 글자를 소문자로 바꾸어 관리 → myBean으로 등록됨

 

다양한 역할 수행

  • 데이터 액세스 계층의 리포지토리
  • 웹 요청을 처리하는 컨트롤러
  • 서비스 계층의 비즈니스 로직
  • ...

> Spring 컨테이너는 이러한 Bean들을 생성하고, 의존성 주입을 통해 서로 연결시켜줌

 

Bean의 설정

 

1. XML 설정에서의 빈 식별자와 별칭 지정 방법

 

빈 아이디, 빈 이름 : 특정 빈을 구분해서 가리키기 위한 빈 식별자(identifier)

빈은 하나 또는 그 이상의 식별자를 가질 수 있음 > id, name 속성을 동시에 지닐 수 있음

** id는 문서 전체에서 고유해야 하며, name은 여러 개를 지정할 수 있음

** 하나 이상의 빈 name을 부여할 때는 콤마(,)나 세미콜론(;)을 이용하여 구분

<bean name="1234,/hello;헬로우" class="...">

** 빈의 이름을 여러 개를 주면 같은 빈이지만 다른 이름으로 참조할 수 있음

 

2. 애노테이션을 이용한 빈 지정 방법

 

1) @Component와 같은 스테레오 타입의 애노테이션을 부여

→ 빈 스캐너에 의해 자동 인식되어 빈으로 등록됨

** 클래스 이름을 그대로 빈 이름으로 사용하면서 첫 글자만 소문자로 바꾸어 자동등록됨

** UserService에 @Component 부여 → userService라는 이름의 빈 자동 등록

 

2) 애노테이션을 이용하지만 직접 빈 이름 지정

→ 스테레오 타입 애노테이션의 디폴트 엘리먼트 값으로 이름을 지정

@Component("myUserService")
public class UserService{

}
@Component
@Named("myUserService")
public class UserService{

}

 

3) @Configuration 애노테이션이 달린 클래스에 @Bean 애노테이션을 붙인 메소드 생성

@Configuration
public class Config{

    @Bean
    public UserDto userDto(){
    
    }
    
}

** 메소드 이름이 그대로 빈 이름이 됨

** userDto라는 이름의 빈 생성

 

Bean의 생명주기

 

1. 생성 (Initialization)

  • 인스턴스화 : Spring 컨테이너가 Bean 정의를 바탕으로 객체의 인스턴스를 생성
  • 의존성 주입 : 생성된 Bean에 필요한 의존성을 주입 (생성자 주입 / setter 주입 / 필드 주입)
  • 초기화 콜백 : 컨테이너는 Bean의 초기화 작업을 수행
    • InitializingBean 인터페이스를 구현한 afterPropertiesSet 메소드 호출
    • XML 구성의 init-method 호출
    • @PostConstruct 애너테이션이 붙은 메소드 호출

2. 사용 (Running)

  • 사용 단계에서의 Bean은 완전히 초기화되어 있음
  • 애플리케이션에서 필요로 할 때마다 Spring 컨테이너로부터 주입받아 사용됨

3. 파괴 (Destruction)

  • 종료 콜백 : 애플리케이션 종료 / 컨테이너 종료 시점에 Bean의 정리 과정이 시작됨
    • DisposableBean 인터페이스를 구현한 destroy 메소드 호출
    • XML 구성의 destroy-method 호출
    • @PreDestroy 애너테이션이 붙은 메소드 호출
  • Bean이 사용한 자원을 해제하고 필요한 종료 작업을 수행
  • 소멸 : 모든 종료 콜백이 실행된 후 Bean 인스턴스는 가비지 컬렉션의 대상이 되어 시스템에서 제거됨

ex 1)

서비스 클래스 MyService 정의

이 클래스의 객체가 생성될 때 / 소멸될 때 실행될 메소드에 @PostConstruct / @PreDestroy 애너테이션 지정

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

public class MyService {

    public MyService() {
        System.out.println("MyService 인스턴스 생성");
    }

    @PostConstruct
    public void init(){
    	// 초기화 작업
        System.out.println("MyService 초기화 작업 실행");
    }

    public void serviceMethod(){
        // 서비스 메소드 실행 로직
        System.out.println("MyService의 serviceMethod 실행");
    }
    
    @PreDestroy
    public void destroy(){
        // 소멸 전 처리 작업
        System.out.println("MyService 소멸 전 처리 작업 실행");
    }
}

 

Spring의 구성 파일 (applicationContext.xml) 또는 Java 구성 클래스를 사용하여 MyService를 Bean으로 등록

Java 기반의 구성 > @Configuration 애너테이션 사용하여 구성 클래스 정의

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

@Configuration
public class AppConfig{
	
    @Bean
    public MyService myService(){
		return new MyService();
	}
}

 

Spring 애플리케이션 실행하여 실행 중에 MyService Bean의 serviceMethod를 호출

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main{

    // Spring 컨테이너 생성 및 구성 클래스 등록
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

    // Bean 가져오기 및 메소드 호출
    MyService myService = context.getBean(MyService.class);
    myService.serviceMethod();
    
    // 컨테이너 종료
    context.close();
}

 

Bean 인스턴스 생성 (생성자) 

@PostConstruct 애너테이션 붙은 init 메소드

myServiceMethod

@PreDestroy 애너테이션 붙은 destroy 메소드

컨테이너 종료

 

ex 2)

애너테이션을 통한 Bean 등록 / 초기화 콜백 등록 / 종료 콜백 등록

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

@Component
public class LoginService {

    private UserAuthenticationManager authenticationManager;

    public LoginService() {
        // 기본 생성자
    }

    @PostConstruct
    public void init() {
        // 로그인 서비스 관련 초기화 작업
        this.authenticationManager = new UserAuthenticationManager();
        // 필요한 초기 데이터 로드 또는 초기화 로직 실행
        System.out.println("LoginService 초기화 완료. 필요한 자원을 로드하였습니다.");
    }

    public boolean login(String username, String password) {
        // 사용자 인증 로직 구현
        return authenticationManager.authenticate(username, password);
    }

    @PreDestroy
    public void cleanup() {
        // 로그인 서비스 종료 전 자원 정리 작업
        // 예를 들어, 캐시된 사용자 데이터를 정리하거나, 외부 시스템과의 연결을 해제
        System.out.println("LoginService 종료 중. 사용된 자원을 정리합니다.");
    }
}

 

ex 3)

InitializingBean, DisposableBean 인터페이스를 구현하여 초기화 콜백, 종료 콜백 등록

afterPropertiesSet 메소드, destroy 메소드를 오버라이딩

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

@Aspect
@Slf4j
@Getter
public class FactorialLoggingAspect implements InitializingBean, DisposableBean {

    private boolean isInitialized;
    private boolean isDestroyed;
    
    @Override
    public void afterPropertiesSet() throws Exception{
        isInitialized = true;
        log.info("FactorialLoggingAspect Bean initialized");
    }  

    @Override
    public void destroy() throws Exception{
        isDestroyed = true;
        log.info("FactorialLoggingAspect Bean destroyed");
    }
    

    @Pointcut("execution(public void com.elice.factorial..factorial(..))")
    private void targetMethod() {};

    @Before("targetMethod()")
    public void logBefore(JoinPoint joinPoint) {
        log.info("[메서드 호출 전] 호출 클래스: " + joinPoint.getTarget().getClass().getSimpleName());
        log.info("[메서드 호출 전] 호출 메서드: " + joinPoint.getSignature().getName());
    }

    @After("targetMethod()")
    public void logAfter(JoinPoint joinPoint) {
        log.info("[메서드 호출 후] 호출 메서드: " + joinPoint.getSignature().getName());
    }
}

 

 

 

 

 

'spring' 카테고리의 다른 글

타임리프(Thymeleaf)  (0) 2024.09.13
템플릿 엔진(Template Engine)  (0) 2024.09.13
MVC 패턴  (0) 2024.04.20
HTTP / REST API  (0) 2024.04.19
스프링 프레임워크 / Spring Boot  (1) 2024.04.19