공부해봅시당

[Spring] 스프링빈(Spring Bean) 본문

STUDY/Spring

[Spring] 스프링빈(Spring Bean)

tngus 2024. 2. 28. 19:52

목차

1. 스프링 빈(Spring Bean)이란?

2. Spring Bean을 Spring IoC Container에 등록하는 방법 - 2가지

3. 스프링 빈 스코프

4. 스프링 빈 생명주기

 

 

1. 스프링 빈(Spring Bean)이란?

Spring IoC 컨테이너가 관리하는 자바 객체를 빈(Bean)이라고 부름

제어의 역전 (IOC, Inversion Of Control)의 특징은 아래와 같음

 

일반적으로 처음에 배우는 자바 프로그램(IoC가 적용되지 않은 경우)

각 객체들이 프로그램의 흐름을 결정하고 각 객체를 직접 생성하고 조작하는 작업(객체를 직접 생성하여 메소드 호출)을 했음

즉, 모든 작업을 사용자가 제어하는 구조

예를 들어 A 객체에서 B 객체에 있는 메소드를 사용하고 싶으면, B 객체를 직접 A 객체 내에서 생성하고 메소드를 호출

 

IoC가 적용된 경우

IOC가 적용된 경우, 객체의 생성을 특별한 관리 위임 주체에게 맡김

이 경우 사용자는 객체를 직접 생성하지 않고, 객체의 생명주기를 컨트롤하는 주체는 다른 주체가 됨

즉, 사용자의 제어권을 다른 주체에게 넘기는 것을 IOC(제어의 역전)라고 함

 

 

Spring은 IoC가 적용됨

우리가 알던 기존의 Java Programming 에서는 Class를 생성하고 new를 입력하여 원하는 객체를 직접 생성한 후에 사용

 

Spring에서는 직접 new를 이용하여 생성한 객체가 아니라, Spring에 의하여 관리되는 자바 객체를 사용

이렇게 Spring에 의하여 생성되고 관리되는 자바 객체를 Bean이라고 함

Spring Framework 에서는 Spring Bean 을 얻기 위하여 ApplicationContext.getBean() 와 같은 메소드를 사용하여

Spring 에서 직접 자바 객체를 얻어서 사용

 

 


2. Spring Bean을 Spring IoC Container에 등록하는 방법 - 2가지

(XML로 스프링 빈을 등록하는 방법도 있지만, 최근에는 거의 사용하지 않음)

 

2-1. 컴포넌트 스캔과 자동 의존관계 설정

스프링 부트에서 사용자 클래스를 스프링 빈으로 등록하는 가장 쉬운 방법은 클래스 선언부 위에

@Component 어노테이션을 사용하는 것

 

@Controller, @Service, @Repository는 모두 @Component를 포함하고 있으며

해당 어노테이션으로 등록된 클래스들은 스프링 컨테이너에 의해 자동으로 생성되어 스프링 빈으로 등록

 

2-2. 자바 코드로 직접 스프링 빈 등록

이번에는 수동으로 스프링 빈을 등록하는 방법임
수동으로 스프링 빈을 등록하려면 자바 설정 클래스를 만들어 사용 해야함
 
설정 클래스를 만들고  @Configuration 어노테이션을 클래스 선언부 위에 추가
그리고 특정 타입을 리턴하는 메소드를 만들고, @Bean 어노테이션을 붙여주면 자동으로 해당 타입의 빈 객체가 생성됨 

 

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

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

 

둘 중 어떤 걸 사용해야 할까 고민된다면?

어노테이션 하나로 해결되는 1번 방법이 간단하고 많이 사용되고 있지만, 상황에 따라 2번 방법도 사용됨

 

1번방법을 사용해서 개발을 진행하다 MemberRepository를 변경해야 할 상황이 생기면 1번 방법은 일일이 변경해줘야 하지만,

2번 방법을 사용하면 다른건 건들일 필요 없이 @Configuration에 등록된 @Bean만 수정해주면 되므로, 수정이 용이함

 

즉, 각자 장단점이 존재함

 

하지만 스프링 부트의 경우 @Component, @Service, @Controller, @Repository, @Bean, @Configuration 등으로
빈들을 등록하고 필요한 곳에서 @Autowired를 통해 의존성 주입을 받아 사용하는 것이 일반적인 방법이라고 할 수 있음


등록된 빈을 사용하는 방법은 링크 참조

 


3. 스프링 빈 스코프

빈 스코프는 말 그대로 빈이 존재할 수 있는 범위

스프링은 다음과 같은 다양한 스코프를 지원

  • 싱글톤(Singleton) : 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
  • 프로토타입(Prototype) : 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프
  • 웹 관련 스코프
    • request : 웹 요청이 들어오고 나갈 때까지 유지되는 스코프
    • session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
    • application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프

3-1. 싱글톤 스코프

 

싱글톤 스코프의 빈을 조회하면 스프링 컨테이너는 항상 같은 인스턴스의 빈을 반환

3-2. 프로토타입 스코프

프로토타입 스코프의 빈을 조회하면 스프링 컨테이너는 항상 새로운 인스턴스를 생성해서 반환
 
프로토타입의 빈을 스프링 컨테이너에 요청하면 스프링 컨테이너는 프로토타입의 빈을 생성하고, 필요한 의존관계를 주입

 

싱글톤 빈은 컨테이너 생성 시점에 같이 생성되고 초기화되지만,

프로토타입 빈은 스프링 컨테이너에서 빈을 조회할 때 생성되고 초기화 메서드도 실행됨
 
스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계 주입, 초기화까지만 처리

클라이언트에게 빈을 반환한 이후에는 생성된 프로토타입 빈을 관리하지 않음

프로토타입 빈을 관리할 책임은 클라이언트에게 있음

 

따라서 @PreDestory와 같은 종료 콜백 메서드가 호출되지 않음

프로토타입 빈의 문제점

만약 싱글톤 빈과 프로토타입 빈을 같이 사용하면?

 

위 그림처럼 clientBean이 프로토타입 빈을 포함한다고 가정하면

clientBean은 싱글톤이므로, 스프링 컨테이너 생성 시점에 함께 생성되고, 의존관계 주입도 발생함
 
1. clientBean은 의존관계 자동 주입을 사용.

    주입 시점에 스프링 컨테이너에게 프로토타입 빈을 요청.
2. 스프링 컨테이너는 프로토타입 빈에 대한 요청을 받고, 프로토타입 빈을 생성해서 clientBean에게 반환.

    프로토타입 빈의 count 필드 값은 0. (여기서 count는 이해를 돕기 위해 임의로 만든 필드임)
 
clientBean은 프로토타입 빈을 내부 필드에 보관함

클라이언트 A는 clientBean을 스프링 컨테이너에 요청해서 받음

싱글톤이므로 항상 같은 clientBean이 반환됨
 
3. 클라이언트 A는 clientBean.logic()을 호출함
4. clientBean은 prototypeBean의 addCount()를 호출해서 프로토타입 빈의 count를 증가시킴.

     count값이 1이 됨.

클라이언트 B는 clientBean을 스프링 컨테이너에 요청해서 받음

싱글톤이므로 항상 같은 clientBean이 반환됨

여기서 중요한 점은, clientBean이 내부에 가지고 있는 프로토타입 빈은 이미 과거에 주입이 끝난 빈...!

주입 시점에 스프링 컨테이너에 요청해서 프로토타입 빈이 새로 생성된 것이지, 사용할 때마다 새로 생성되는 것이 아니다!
 
5. 클라이언트 B는 clientBean.logic()을 호출함
6. clientBean은 prototypeBean의 addCount()를 호출해서 프로토타입 빈의 count를 증가시킴

     원래 count값이 1이었으므로 2가 됨
 
각 요청마다 count 값을 1로 반환받는 것을 기대했지만 2를 반환받게 됨

결국 싱글톤과 프로토타입을 함께 사용하는 경우 기대한 대로 동작하지 않음ㅠㅠ
 
스프링은 일반적으로 싱글톤 빈을 사용하므로, 싱글톤 빈이 프로토타입 빈을 사용하게 됨
그런데 싱글톤 빈은 생성 시점에만 의존관계를 주입받음


따라서 스프링 싱글톤 빈이 생성되는 시점에 프로토 타입 빈도 새로 생성되어서 주입되긴 하지만,

싱글톤 빈과 함께 계속 유지되는 것이 문제!!
 
따라서 프로토타입 빈을 주입 시점에만 새로 생성하는 것이 아니라, 사용할 때마다 새로 생성해서 사용해야 함

Dependency Lookup(DL)과 Provider

싱글톤 빈과 프로토타입 빈을 같이 사용할 때, 프로토타입 빈을 사용할 때마다 항상 새로운 프로토타입 빈을 생성하려면 어떻게 해야 할까? 
 
가장 간단한 방법은 싱글톤 빈이 프로토타입을 사용할 때마다 스프링 컨테이너에 새로 요청하는 것!

@Component
class ClientBean {
    
    //ClientBean에서 applicationContext 자체를 주입받아서 직접 요청한다.
    @Autowired
    private ApplicationContext ac;
    
    public int logic() {
        PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
        prototypeBean.addCount();
        return prototypeBean.getCount();
    }
}

@Component
@Scope("prototype")
class PrototypeBean {
    
    private int count = 0;
    
    public void addCount() {
        count++;
    }
    
    public int getCount() {
        return count;
    }
    
    @PostConstruct
    public void init() {
        System.out.println("PrototypeBean.init" + this);
    }
    
    @PreDestroy
    public void destroy() {
        System.out.println("PrototypeBean.destroy");
    }
}

 

위 코드는 실행 시 ac.getBean()을 통해서 항상 새로운 프로토타입 빈이 생성됨

 

이처럼 의존관계를 외부에서 주입(DI) 받는 것이 아니라

직접 필요한 의존관계를 찾는 것을 Dependecy Lookup(DL), 의존관계 조회(탐색)이라고 함

 

그런데 이렇게 스프링 애플리케이션 컨텍스트 전체를 주입받게 되면,

스프링 컨테이너에 종속적인 코드가 되고, 단위 테스트도 어려워짐

 

따라서 스프링 애플리케이션 컨텍스트 전체를 주입받는 대신,

ObjectProvider를 주입받아서 사용하는 것이 더 나은 방법!

 

ObjectProvider는 지정한 빈을 컨테이너에서 대신 찾아주는 DL(Dependency Lookup) 서비스를 제공하는 것

(참고로 과거에는 ObjectFactory가 있었는데, 여기에 편의 기능을 추가해서 ObjectProvider가 만들어졌다고 함)

@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;

public int logic() {
    PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
    prototypeBean.addCount();
    int count = prototypeBean.getCount();
    return count;
}

 

실행하면 prototypeBeanProvider.getObject()를 통해서 항상 새로운 프로토타입 빈이 생성되는 것을 알 수 있음


ObjectProvider의 getObject()를 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환함(DL)


스프링이 제공하는 기능을 사용하지만, 기능이 단순하므로 단위 테스트를 만들거나 mock 코드를 만들기는 훨씬 쉬워짐


스프링에 의존하지만 별도 라이브러리는 필요 없음
ObjectProvider는 DL정도의 기능만 제공함

3-3. 웹 스코프

웹 스코프는 웹 환경에서만 동작함

웹 스코프는 프로토타입과 다르게 스프링이 해당 스코프의 종료 시점까지 관리함

따라서 종료 메서드가 호출됨

 

웹 스코프는 다음과 같은 종류가 있음
 



- request : HTTP 요청 하나가 들어오고 나갈 때까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리됨
- session : HTTP Session과 동일한 생명주기를 가지는 스코프
- application : 서블릿 컨텍스트(ServletContext)와 동일한 생명주기를 가지는 스코프
- websocket : 웹 소켓과 동일한 생명주기를 가지는 스코프

 
스프링 빈 등록 시 웹 스코프를 그대로 주입받으면 오류가 발생함

 

싱글톤 빈은 스프링 컨테이너 생성 시 함께 생성되어서 라이프 사이클을 같이 함

 

하지만 웹 스코프(여기서는 request 스코프)는 HTTP 요청이 올 때 새로 생성되고 응답하면 사라지기 때문에,

싱글톤 빈이 생성되는 시점에는 아직 생성되지 않아 의존관계 주입이 불가능함
 
이때 프록시를 사용하면 문제를 해결할 수 있음

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_ClASS)
public class MyLogger {
}

 

@Scope속성에 proxyMode = ScopedProxyMode.TARGET_CLASS를 추가

적용대상이 인터페이스가 아닌 클래스면 TARGET_CLASS를 선택
적용대상이 인터페이스면 INTERFACES를 선택

이렇게 하면 MyLogger의 가짜 프록시 클래스를 만들어두고

HTTP request와 상관없이 가짜 프록시 클래스를 다른 빈에 미리 주입해둘 수 있음


Scope의 proxyMode = ScopedProxyMode.TARGET_CLASS를 설정하면

스프링 컨테이너는 CGLIB라는 바이트코드 조작 라이브러리를 사용해서,

해당 웹 스코프 빈 클래스를 상속하는 가짜 프록시 객체를 생성함


위의 결과를 보면 내가 등록한 순수 자바 클래스 빈이 아니라 가짜 프록시 객체가 등록된 것을 확인할 수 있음


그리고 스프링 컨테이너에 원래 만들었던 진짜 클래스 이름으로 (첫 문자 소문자, 빈 이름 규칙) (여기서는 myLogger)

진짜 대신에 이 가짜 프록시 객체를 등록함


ag.getBean("myLogger", MyLogger.class)로 조회해도 프록시 객체가 조회됨
그래서 의존관계 주입도 이 가짜 프록시 객체가 주입됨

가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직이 들어있음
가짜 프록시 객체는 내부에 진짜 myLogger를 찾는 방법을 알고 있음
클라이언트가 myLogger.logic()을 호출하면 사실은 가짜 프록시 객체의 메서드를 호출한 것!


가짜 프록시 객체는 request 스코프의 진짜 myLogger.logic()을 호출함
가짜 프록시 객체는 원본 클래스를 상속받아서 만들어졌기 때문에 이 객체를 사용하는 클라이언트 입장에서는 원본인지 아닌지도 모르게 동일하게 사용할 수 있음(다형성)


4. 스프링 빈 생명주기

앞서 스프링의 IoC 컨테이너는 Bean 객체들을 책임지고 의존성을 관리한다고 함

 

객체들을 관리한다는 것은

객체의 생성부터 소멸까지의 생명주기(LifeCycle) 관리를 개발자가 아닌 컨테이너가 대신 해준다는 의미

 

객체 관리의 주체가 프레임워크(Container)가 되기 때문에 개발자는 로직에 집중할 수 있음

 

여기서 우리는 직접 우리가 관리하지 않기 때문에 스프링 빈의 생명주기가 어떻게 되는지 알아야 할 필요성이 있음

 

4-1. 빈 생명주기 콜백의 필요성

먼저 콜백에 대해 설명하면,

주로 콜백함수를 부를 때 사용되는 용어이며

콜백함수를 등록하면 특정 이벤트가 발생했을 때 해당 메소드가 호출됨

 

즉, 조건에 따라 실행될 수도 실행되지 않을 수도 있는 개념

 

보통 프로젝트를 하다보면 DB연결, 네트워크 소켓 연결 등과 같이

시작 시점에 미리 연결한 뒤 어플리케이션 종료시점에 연결을 종료해야 하는 경우

객체의 초기화 및 종료 작업이 필요할 것

(Ex. 커넥션 풀의 connect & disconnect)

 

스프링 빈도 위와 같은 원리로 초기화 작업과 종료 작업이 나눠서 진행됨

간단히 말해서 객체 생성 → 의존관계 주입이라는 라이프사이클을 가짐

 

즉, 스프링 빈은 의존관계 주입이 다 끝난 다음에야 필요한 데이터를 사용할 수 있는 준비가 완료됨

- 초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
- 소멸전 콜백: 빈이 소멸되기 직전 호출

 

4-2. 의존성 주입 과정

 

가장 처음에는 Spring IoC 컨테이너가 만들어지는 과정이 일어남

 

위의 그림은 SpringBoot에서 Component-Scan으로 Bean 등록을 시작하는 과정을 그림으로 표현한 것
(위에서 '스프링 빈을 등록하는 2가지 방법'에 대해 다루었으니 스크롤을 올려 확인해보길 바람)

 

 

위와 같이 @Configuration 방법을 통해

Bean으로 등록할 수 있는 어노테이션들과 설정파일들을 읽어 IoC 컨테이너 안에 Bean으로 등록 시킴

 

그리고 의존 관계를 주입하기 전의 준비 단계가 존재함

이 단계에서 객체의 생성이 일어남

 

여기서 한 가지 알고 넘어가야 할 부분이 있음
- 생성자 주입 : 객체의 생성과 의존관계 주입이 동시에 일어남
- Setter, Field 주입 : 객체의 생성 ㅡ> 의존관계 주입으로 라이프 사이클이 나누어져 있음

 

 

1) 생성자 주입

즉, 생성자 주입은 위의 그림이 동시에 진행된다는 뜻임

왜 생성자 주입은 동시에 일어나는 것일까?

@Controller
public class CocoController{
	private final CocoService cocoService;
    
    public CocoController(CocoService cocoService) {
    	this.cocoService = cocoService;
    }
}

 

 

자바에서 new 연산을 호출하면 생성자가 호출됨

Controller 클래스에 존재하는 Service 클래스와의 의존관계가 존재하지 않는다면,

다음과 같이 Controller 클래스는 객체 생성이 불가능함

 

public class Main {
	public static void main(String[] args) {
    	// CocoController controller = new CocoContoller(); // 컴파일 에러
        
    	CocoContoroller controller1 = new CocoController(new CocoService());
    }
}

그렇기 때문에 생성자 주입에서는 객체 생성, 의존관계 주입이 하나의 단계에서 일어나는 것!

 

이를 통해 얻는 이점은 다음과 같음

1. null을 주입하지 않는 한 NullPointerException은 발생하지 않음

2. 의존관계를 주입하지 않은 경우 객체를 생성할 수 없음

즉, 의존관계에 대한 내용을 외부로 노출시킴으로써 컴파일 타임에 오류를 잡아낼 수 있음

 

2) Setter 주입

@Contoller
public class CocoController {
	private CocoService cocoService;
    
    @Autowired
    public void setCocoService(CocoService cocoService) {
    	this.cocoService = cocoService;
    }
}

setter 주입의 경우 Controller 객체를 만들 때 의존 관계는 필요하지 않음

즉, 생성자 주입과는 다르게 Controller 객체를 만들때 Service 객체와 의존 관계가 없어도

Controller 객체를 만들 수 있음

 

따라서 객체 생성 → 의존 관계 주입의 단계로 나누어서 Bean LifeCycle 이 진행됨

 

 

그래서 위와 같이 코드에 작성한 의존관계를 보고 IoC 컨테이너에서 의존성 주입을 해줌

 

4-3. 스프링 빈 이벤트 라이프 사이클

1. 스프링 IoC 컨테이너 생성
2. 스프링 빈 생성
3. 의존관계 주입
4. 초기화 콜백 메소드 호출 (빈의 의존관계 주입이 완료된 후 호출)
5. 사용
6. 소멸 전 콜백 메소드 호출 (빈 소멸 직전에 호출)
7. 스프링 종료

 

 

스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메소드를 통해 초기화 시점을 알려줌

스프링 컨테이너가 종료되기 직전에도 소멸 콜백 메소드를 통해 소멸 시점을 알려줌

 

그렇다면 스프링 빈 라이프 사이클을 압축시키기 위해
생성자 주입을 통해 빈 생성과 초기화를 동시에 진행하면 되지 않을까?


생성자는 파라미터를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가짐

반면에 초기화는 이렇게 생성된 값들을 활용해서 외부 커넥션을 연결하는 등 무거운 동작을 수행함

 

생성과 초기화를 묶는 것은 SRP(단일 책임 원칙)적으로도 과한 책임을 가지게 됨

 

따라서 생성자 안에서 무거운 초기화 작업을 함께 하는 것보다는

객체를 생성하는 부분과 초기화 하는 부분을 명확하게 나누는 것이 객체지향 프로그램적 관점과 유지보수 관점에서 좋음

 

물론, 초기화 작업이 내부 값들만 약간 변경하는 정도로 단순한 경우에는 생성자에서 한번에 처리하는게 나을 수 있음

객체의 생성과 초기화를 분리하자!

객체 생성

이 단계에서는 객체가 필요로 하는 메모리 공간을 확보하고, 객체를 실제로 만드는 일을 수행
여기서는 객체가 어떤 데이터를 담을 것인지 정의하는데, 이때 필요한 데이터(파라미터)를 생성자 함수에 전달함

객체 초기화
객체가 만들어진 후, 이 객체가 다른 외부 시스템이나 네트워크, 데이터베이스 등과 연결되어야 하는 경우가 있음
이러한 '무거운' 작업들은 객체가 만들어진 후에 수행하는 것이 좋음
이 초기화 단계에서는 객체가 제대로 작동하기 위해 필요한 추가 설정을 함

 

객체의 생성과 초기화를 분리하는 예제는 아래와 같음

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;

@Component
public class DatabaseConnection {

    private String url;
    private String username;
    private String password;

    // 생성자 주입을 통해 빈 생성
    public DatabaseConnection(String url, String username, String password) {
        this.url = url;
        this.username = username;
        this.password = password;
    }

    // 초기화 메서드
    @PostConstruct
    public void initializeConnection() {
        // 여기서는 예시로 초기화 과정을 간단히 표현합니다.
        // 실제로는 여기서 데이터베이스 연결을 초기화하는 로직이 들어갈 것입니다.
        System.out.println("Connecting to database at " + url + " with user " + username);
        // 데이터베이스 연결 초기화 로직
    }

    // 사용 예시
    public void doSomethingWithDatabase() {
        // 데이터베이스와의 상호작용
    }
    
    // 소멸 메서드
    @PreDestroy
    public void closeConnection() {
        // 데이터베이스 연결 종료 로직
        System.out.println("Closing connection to database");
    }
}

 

 

4-4. 빈 생명주기 콜백 3가지

스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 관리함

1. 인터페이스( InitializingBean, DisposableBean )

2. 설정 정보에 초기화 메소드, 종료 메소드 지정

3. @PostConstruct, @PreDestroy 어노테이션 지원

1) 인터페이스(InitialingBean, DisposableBean)

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

- InitalizingBean은 afterPropertiesSet() 메소드로 초기화를 지원한다. (의존관계 주입이 끝난 후에 초기화 진행)
- DisposableBean은 destory() 메소드로 소멸을 지원한다. (Bean 종료 전에 마무리 작업, 예를 들면 자원 해제(close() 등))

단점
- InitalizingBean, DisposableBean 인터페이스는 스프링 전용 인터페이스

   해당 코드가 인터페이스에 의존함
- 초기화, 소멸 메소드를 오버라이드 하기 때문에 메소드명을 변경할 수 없음
- 코드를 커스터마이징 할 수 없는 외부 라이브러리에 적용 불가능함

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

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

@Configuration
class LifeCycleConfig {
	@Bean(initMethod = "initialize", destroyMethod = "close")
	pubic ExampleBean 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 {
    	// 소멸 전 콜백 (메모리 반납, 연결 종료와 같은 과정)
    }
}

장점

- 최신 스프링에서 가장 권장하는 방법
- 어노테이션 하나만 붙이면 되므로 매우 편리함
- 패키지가 javax.annotation.xxx

   스프링에 종속적인 기술이 아닌 JSR-250이라는 자바 표준

   따라서 스프링이 아닌 다른 컨테이너에서도 동작함
- 컴포넌트 스캔과 잘어울림

단점

- 커스터마이징이 불가능한 외부 라이브러리에서 적용이 불가능
   외부 라이브러리에서 초기화, 종료를 해야 할 경우 두 번째 방법

   즉, @Bean의 initMethod와 destoryMethod 속성을 사용하는 것이 좋음


참조

https://melonicedlatte.com/2021/07/11/232800.html#google_vignette

 

스프링 빈(Spring Bean)이란? 개념 정리 - Easy is Perfect

 

melonicedlatte.com

https://dev-coco.tistory.com/170

 

[Spring] 빈 생명주기(Bean LifeCycle) 콜백 알아보기

스프링의 IoC 컨테이너는 Bean 객체들을 책임지고 의존성을 관리한다. 객체들을 관리한다는 것은 객체의 생성부터 소멸까지의 생명주기(LifeCycle) 관리를 개발자가 아닌 컨테이너가 대신 해준다는

dev-coco.tistory.com

https://code-lab1.tistory.com/186

 

[Spring] 빈 스코프(Bean Scope)란? Dependency Lookup(DL)이란?

빈 스코프(Bean Scope)란? 빈 스코프는 말 그대로 빈이 존재할 수 있는 범위를 뜻한다. 스프링은 다음과 같은 다양한 스코프를 지원한다. 싱글톤(Singleton) : 기본 스코프, 스프링 컨테이너의 시작과

code-lab1.tistory.com

https://dev-coco.tistory.com/69

 

[Spring] 스프링 빈을 등록하는 두 가지 방법(@Component, @Bean)

Bean이 뭘까? 먼저 Bean을 이해하기 위해 스프링 컨테이너 (Spring Container 또는 IoC 컨테이너)에 대해서 알 필요가 있습니다. 자바 어플리케이션은 어플리케이션 동작을 제공하는 객체들로 이루어져

dev-coco.tistory.com

https://catsbi.oopy.io/3a9e3492-f511-483d-bc65-183bb0c166b3

 

빈 생명주기 콜백

목차

catsbi.oopy.io

https://blog.naver.com/writer0713/222938122259

 

[spring] 스프링 빈 이벤트 라이프 사이클

개요 스프링 빈은 라이프 사이클을 가지고 있다. 그리고, 스프링에 의해 호출되는 콜백이 존재한다. 예를 ...

blog.naver.com