[cs] - Spring Bean, Spring Bean 생성 과정

링딩·2023년 4월 23일
1

Computer Science

목록 보기
39/49





1. Spring Bean이란?

  • 스프링 Ioc 컨테이너가 관리하는 자바 객체를 뜻하며 필요할 때마다 IoC 컨테이너에서 가져와서 사용한다.
  • IoC 컨테이너에 의해 생명주기가 관리되는 객체를 의미한다.
  • 사용할 때는 ¹어노테이션@Bean을 사용하거나 ² xml 설정을 통해 일반 객체를 Bean으로 등록이 가능하다.

IoC(Inversion of Control)란?

IoC란 영어 그대로 제어의 역전이라 부른다.
제어의 역전이란 메서드나 객체의 호출 작업을 개발자가 아닌 스프링에게 제어권을 넘기는 것을 말한다.
이전까지는 개발자가 객체의 생성을 관리하며 제어했지만, 스프링을 사용하게 되면 스프링 컨테이너에게 제어권을 넘겨 스프링 컨테이너가 흐름을 제어하게 된다.


IoC 분류

  • DL

    • 저장소에 저장되어 있는 Bean에 접근하기 위해 컨테이너가 제공하는 API를 이용하여 Bean을 Lockup하는 것
  • DI

    • 각 클래스간의 의존관계를 빈 설정(Bean Definition) 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것

DL 사용시 컨테이너 종속이 증가하기 때문에 주로 DI를 사용한다.


< 이해가 안가서 다른 블로그의 글을 인용하였습니다. >

프레임워크를 적용하지 않은, 우리가 그동안 작성해왔던 일반적인 프로그램을 생각해보자. 객체의 생명주기(객체의 생성, 초기화, 소멸, 메서드 호출 등)를 클라이언트 구현 객체가 직접 관리한다. 또한 다른 사람이 작성한 외부 코드(라이브러리)를 호출하더라도 해당 코드의 호출 시점 역시 직접 관리한다. 하지만 이러한 생명주기를 직접 관리하지 않는 경우라면?🤔


스프링과 같은 프레임워크를 사용할 때를 생각해보자. Controller, Service 같은 객체들의 동작을 우리가 직접 구현하기는 하지만, 해당 객체들이 어느 시점에 호출될 지는 신경쓰지 않는다🤷‍♀️. 단지 프레임워크가 요구하는대로 객체를 생성하면, 프레임워크가 해당 객체들을 가져다가 생성하고, 메서드를 호출하고, 소멸시킨다. 프로그램의 제어권이 역전된 것이다.


때문에 프레임워크와 라이브러리는 어떤 차이가 있는지에 대해 IoC를 통해 설명이 가능하다. 라이브러리를 사용하는 어플리케이션은 어플리케이션의 제어 흐름을 라이브러리에 내주지 않는다. 단지 필요한 시점에 라이브러리에 작성된 객체를 적재적소에 가져다 쓸 뿐이다. 하지만 프레임워크를 사용한 어플리케이션의 경우, 어플리케이션 코드에 작성한 객체들을 프레임워크가 필요한 시점에 가져다가 프로그램을 구동하기 때문에 프로그램의 제어권이 프레임워크로 역전된다.



IoC 덕에 우리는 이런 점을 얻을 수 있었다.

  1. 프로그램의 진행 흐름과 구체적인 구현을 분리시킬 수 있다.
  2. 개발자는 비즈니스 로직에 집중할 수 있다.
  3. 구현체 사이의 변경이 용이하다.
  4. 객체 간 의존성이 낮아진다.



IOC 컨테이너란(Spring Container) .. ?

  • 제어의 역전의 약어로 "객체의 생성 및 생명주기에 대한 모든 객체에 대한 제어권이 바뀌었다는 것을 의미"

spring 컨테이너가 ioc 컨테이너의 역할을 하는거지 둘이 완전히 같은게 아님
-> di컨테이너라고도 한다.
=> 그 반대는 적용되지 않는다. 포함하지 않는다.
ioc 컨테이너의 역할을 하려고 하는게 스프링에서는 '스프링컨테이너'가 그 역할을 하는 것


🎉 컨테이너의 사용 역할

1) DI(Dependency Injection, 의존성 주입)
개발자가 객체를 new해서 생성하지않고, Ioc컨테이너에 존재하는 Bean 객체를 주입해준다. 해당 기능을 사용하면 싱글톤, 개발자의 편의, 성능 이슈 등등을 해결해준다.

2) 객체의 Scope
Ioc 컨테이너에 객체의 제어권을 넘겨주면, Ioc 컨테이너가 해당 객체의 Scope를 관리해준다.

3) 라이프 사이클 인터페이스 지원
완벽히 구현되지 않은 클래스를 단위 테스트할 때, 테스팅을 도와준다


🌹 IoC 컨테이너의 DI를 통한 중재 🌹

  1. Container가 로드되면, Bean에 해당하는 객체들을 scan 하여, 해당 Bean들을 생성하려고 한다.
    -> 이 과정에서 의존성 주입이 이루어진다.
    => 만약 순환 참조가 있다면 예외가 발생하여 Application은 종료된다.

  2. 이제 Bean들이 생성되려고 하는데, 사용자가 지정한 init method가 존재한다면, 객체가 생성되고 init이 실행되게 된다.

  3. 그 뒤에 사용자가 지정한 utility method(afterPropertiesSet)과 같은 메서드가 존재한다면, 해당 메서드가 실행되게 된다.(콜백 함수)

  4. 프로그램이 종료되기 전에 이제 Container도 같이 종료되려고 하는데, 이 과정에서 destory 메서드가 존재한다면, 실행하고 정상적으로 종료



🚨 주의점... DI == IOC 는 아니다!!

  • 절대 IoC == DI가 아니다! (DI ⊂ IoC 라면 몰라도) DI 없이도 IoC를 만족하는 프로그램을 만들 수 있다.
  • IoC는 프로그램 제어권을 역전시키는 개념이고, DI는 해당 개념을 구현하기 위해 사용하는 디자인 패턴 중 하나로, 이름 그대로 객체의 의존관계를 외부에서 주입시키는 패턴을 말한다.






2. Spring Bean 등록 방법

웹 프로그래밍은 멀티유저에 대한 동시성 제어가 중요하며 이를 위해 스프링 컨테이너에서는 싱글톤 패턴으로 관리한다.

크게 두 가지 방법이 있는데,

(1) 컴포넌트 스캔

스프링에서 제공하는 @Component 어노테이션으로 클래스를 명시하여 컨테이너가 생성될 때 컴포넌트 스캔을 통해 자동으로 빈에 등록할 수 있다.

  • @Component
    • @Controller
    • @Service
    • @Repository

위 3가지 어노테이션들은 @Component로 구성되어 있어서 컴포넌트 스캔에 이용할 수 있다.

  • (결론): 연관된 어노테이션들을 이용하여 클래스를 명시해주면 스프링 컨테이너는 컴포넌트 스캔을 통해 자동으로 해당 클래스의 객체를 빈(Bean)으로 관리한다.

(2) 직접 스프링 빈 등록

Configuration을 위한 클래스를 하나 만든 후(ex SpringConfig,,,), @Configuration으로 명시를 하면 된다. 그리고 빈으로 등록할 클래스를 @Bean 메소드로 생성하여 객체를 리턴하면 된다.

이렇게 쓰게되면 만일 인터페이스로 구현된 Repository 클래스가 다른 클래스로 변경된다고 했을 경우, 간단히 해당 Config파일에서 찾아 리턴 클래스 객체만을 수정해주면 된다.





3. Spring Bean의 Scope

◽ 정의

Bean Scope는 기본적으로 빈이 존재하는 범위를 뜻한다.

  • Bean의 객체는 기본적으로 singleton의 범위를 가지며 singleton는 스프링 컨테이너의 시작과 종료까지 단 하나의 객체만을 사용하는 방식이다.
  • request, session, global session의 scope는 일반 spring 어플리케이션이 아니라 Spring MVC Web Application에서만 사용된다.

◾ Scope 종류 및 설명

Scope설명
singleton하나의 Bean 정의에 대해 Spring IoC Container에서 단 하나의 객체만 존재한다.
prototype하나의 Bean 정의에 대해 다수의 객체가 존재할 수 있다.
request<웹스코프..> 하나의 Bean 정의에 대해 하나의 HTTP request의 생명주기 안에 단 하나의 객체만 존재한다. 각각의 HTTP request는 자신만의 객체를 가지며 Web-aware Spring ApplicationContext 안에서만 유효한 특징이 있다.
session하나의 Bean 정의에 대해 하나의 HTTP Session의 생명주기 안에서 단 하나의 객체만 존재한다. Web-aware Spring ApplicationContext 안에서만 유효한 특징이 있다.
global session하나의 Bean 정의에 대해 하나의 global HTTP Session의 생명주기 안에서 단 하나의 객체만 존재한다. 일반적으로는 portlet context안에서만 유효하며, Web-aware Spring ApplicationContext 안에서만 유효한 특징이 있다.
  • Bean의 객체 범위를 prototype으로 설정하면 객체를 매번 새롭게 생성한다는 특징이 있다.
    -> prototype으로 설정하면 @Scope 어노테이션@Bean 어노테이션과 함께 사용해야 한다.





그 중에 몇 개의 예시를 들면...

1. Singlenton

singleton bean은 Spring 컨테이너 안에서 딱 한 번 생성되며, 컨테이너가 사라질 때는 bean고 같이 사라진다.

  • 생성된 하나의 인스턴스는 single bean cache에 저장되며 해당 bean에 대한 요청과 참조가 있을 때마다 캐시된 객체를 반환한다.
  • 기본적으로 모든 bean은 scope이 명시적이지 않으면 singleton scope을 가진다.
// xml 설정
<bean id="..." class="..." scope="singleton"></bean>

// annotaion 설정 (대상 클래스에 적용)
@Scope("singleton")

2. Prototype

  • prototype bean은 모든 요청에서 새로운 객체로 생성하는 것이며, 의존성 관계의 bean이 주입될 때마다 새로운 객체가 생성되어 주입된다.
  • GC에 의해 bean이 자동으로 제거된다.
// xml 설정
<bean id="..." class="..." scope="prototype"></bean>

// annotaion 설정 (대상 클래스에 적용)
@Scope("prototype")



Spring Bean 생명주기(Life Cycle)

Feat (마지막

1. 스프링 IoC 컨테이너 생성 → 2. 스프링 빈 생성 → 3. 의존관계 주입 → 4. 초기화 콜백 메소드 호출 → 5. 사용 → 6. 소멸 전 콜백 메소드 호출 → 7. 스프링 종료 과정의 생명 주기를 가지고 있다.

  • Bean은 스프링 컨테이너에 의해 생명주기를 관리한다.

스프링은 "의존관계 주입이 완료되면"... 스프링 빈에게 콜백 메소드를 통해 초기화 시점을 알려주며,
스프링 컨테이너가 종료되기 직전에도 소멸 콜백 메소드를 통해 소멸 시점을 알려준다.


Bean 생명주기 콜백 3가지 방법

  1. 인터페이스( InitializingBean, DisposableBean )
  2. 설정 정보에 초기화 메소드, 종료 메소드 지정
  3. @PostConstruct, @PreDestroy 어노테이션 지원

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

public class ExampleBean implements InitializingBean, DisposableBean {
 
    @Override
    public void afterPropertiesSet() throws Exception {
        // 초기화 콜백 (의존관계 주입이 끝나면 호출)
    }
 
    @Override
    public void destroy() throws Exception {
        // 소멸 전 콜백 (Bean 종료 전 메모리 반납, 연결 종료와 같은 과정)
    }
}
  • 빈 객체의 클래스가 InitializingBean Interface 또는 DisposableBean을 구현하고 있으며 해당 인터페이스에서 정의된 메소드를 호출해 빈 객체의 초기화 또는 종료를 수행한다.

(단점)

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

현재는 거의 쓰지 않음( 초창기 방법임)


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

public class ExampleBean {
 
    public void initialize() throws Exception {
        // 초기화 콜백 (의존관계 주입이 끝나면 호출)
    }
 
    public void close() throws Exception {
        // 소멸 전 콜백 (메모리 반납, 연결 종료와 같은 과정)
    }
}
 
@Configuration
class LifeCycleConfig {
 
    @Bean(initMethod = "initialize", destroyMethod = "close")
    public ExampleBean exampleBean() {
        // 생략
    }
}

장점

  1. 메소드명을 자유롭게 부여 가능하다.
  2. 스프링 코드에 의존하지 않는다.
  3. 설정 정보를 사용하기 때문에 코드를 커스터마이징 할 수 없는 외부라이브러리에서도 적용 가능하다.

단점

  • Bean 지정시 initMethoddestoryMethod직접 지정해야 하기에 번거롭다.

+) 참고

  • @BeandestoryMethod는 기본값이 inferred(추론)으로 등록
    • 즉, close, shutdown이라는 이름의 메소드가 '종료 메소드이군~!'이라고 추론하고 🤔 자동으로 호출해준다.👍
      => 즉, 종료 메소드를 따로 부여하지 않더라도 잘 작동한다.



3. 어노테이션을 이용한 빈 초기화 방법에는 @PostConstruct빈 소멸에서는 @PreDestory를 사용한다.

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

장점

  • 최신 스프링에서 가장 권장하는 방법이다.
  1. 어노테이션 하나만 붙이면 되므로 매우 편리하다.
  2. 패키지가 javax.annotation.xxx 이다.
    -> 자바 표준이다. 따라서 스프링이 아닌 다른 컨테이너에서도 동작한다.
  3. 컴포넌트 스캔과 잘 어울린다.

단점

  • 커스터마이징이 불가능한 외부 라이브러리에서 적용이 불가능하다.

    외부 라이브러리에서 초기화, 종료를 해야 할 경우 두 번째 방법 즉, @BeaninitMethoddestoryMethod 속성을 사용하자.




@Bean과 @Component 차이점

"둘다 빈으로 등록되게 하고 관리되게 해주는 것이라던데,,, 빈을 생성하는데 @Bean과 @Component 어노테이션 둘의 차이 점은 무엇일까?" 🤔

1. 왜(why) 사용하는가?

A. 특정 객체를 의존하는 다른 객체들에게 의존성을 주입하기 위해서입니다.

이를 위해서 Spring IoC 컨테이너에서 관리할 수 있도록, 즉 ApplicationContext가 관리하는 bean으로 등록하기 위해서 이런 어노테이션을 붙이는 것이고, 해당 어노테이션이 붙은 객체들을 의존하는 다른 객체들이 컨테이너가 빈으로 등록하여 관리하는 의존성을 @Autowired를 선언함으로써 주입받을 수 있습니다.


2. @Bean과 @Component 사용하는 방법 (How)

  • @Bean
    • @Configuration이나 @Component 어노테이션이 붙은 클래스 내에 메서드 레벨에서 사용
  • @Component
    • 클래스 레벨에서 사용

결론적으로 차이점은

@Bean개발자가 직접 작성하지 않은 서드파티 라이브러리의 설정과 같은 의존성을 주입받기 위해서 사용하기에 적절하고,(@Configuration이 붙은 설정 파일에 주로 선언함)
@Component개발자가 직.접. 작성한 코드에 있는 의존성을 주입받기 위해서 사용하기에 적절




면접 때 나올 수 있는 질문

  • 싱글톤 레지스트리를 통해서 싱글톤 객체를 생성만 해주는거지 동기화 같은것은 개발자가 해줘야 함 => 싱글톤 빈은 thread-safe하지 않다.
  • 컴포넌트 스캔은 스프링 스캔을 어디서부터 시작하는지



출처

developer-ellen님의 글을 바탕으로 작성하였습니다.

dev-coco님의 이 글을 바탕으로 작성하였습니다.
dev-coco님의 이 글을 바탕으로 작성하였습니다.

profile
초짜 백엔드 개린이

0개의 댓글