스프링 빈 (Spring Bean) 이란?
💡 스프링 컨테이너에 의해 관리되는 재사용 가능한 소프트웨어 컴포넌트이다.
즉, 스프링 컨테이너가 관리하는 자바 객체를 의미하며, 하나 이상의 빈 (Bean)을 관리한다.
빈과 관련된 블로그 포스팅이 있으니 참고하시길 바랍니다.
[Spring] IOC, DI, DL 란?
IOC (제어의 역전)란? 💡 IOC (Inversion of Control) 의 약자로 제어의 역전이라는 의미를 갖는다. 이는 객체의 생성부터 소멸까지 생명 주기의 관리를 개발자가 아닌, 외부에서 꽌리하는 것을 의미한다
hyun-dev-com.tistory.com
빈 라이플 사이클 (Bean Life Cycle)
💡 해당 자바 객체 (Bean)가 언제 어떻게 생성되어 소멸되기 전까지 어떤 작업을 수행하고,
어떻게 소멸되는지 일련의 과정을 이르는 말이다.
스프링 컨테이너는 이런 빈 객체의 생명주기를 컨테이너의 생명주기 내에서 관리하고,
생성, 소멸 시 호출될 수 있는 콜백 메서드를 제공하고 있다.
➡️ 스프링 컨테이너 생성
➡️ 스프링 빈 생성
➡️ 의존성 주입
➡️ 초기화 콜백 : 빈 의존관계 주입이 완료된 후 호출된다.
➡️ 사용
➡️ 소멸전 콜백 : 빈이 소멸되기 전 호출된다.
➡️ 스프링 종료
⚠️ 객체의 생성과 초기화를 분리해야한다.
생성자는 필수 정보(파라미터)를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가진다.
반면에 초기화는 생성된 값들을 활용해서 다른 작업을 수행한다.
따라서 생성자 안에서 다른 작업을 함꼐 하는것 보다는 객체를 생성하는 부분과
초기화 하는 부분을 명확하게 나누는 것이 유지보수 관점에 좋다.
빈 생명주기 콜백 (Bean Life Cycle Call back)
💡 스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원한다.
➡️ 인터페이스 (InitializingBean, DisposableBean)
➡️ 설정 정보에 초기화 메서드, 종료 메서드 지정
➡️ @PostConstruct, @PreDestroy 어노테이션 지원
1. 인터페이스 (InitializingBean, DisposableBean)
/**
* 1. interface 방법으로 bean life-cycle 초기화, 소멸에 대한 부분을 담당한다.
* 요즘은 거의 사용하지 않는 방법이다.
* InitializingBean => bean 의존관계 설정 후 실행되는 함수이다.
* DisposableBean => 종료 전 콜백함수로 실행된다.
* */
public class InterfaceNetworkClient implements InitializingBean, DisposableBean {
private String url;
public InterfaceNetworkClient() {
System.out.println("생성자 호출, url = " + url);
}
public void setUrl(String url) {
this.url = url;
}
//서비스 시작시 호출
public void connect() {
System.out.println("connect: " + url);
}
public void call(String message) {
System.out.println("call: " + url + " message = " + message);
}
//서비스 종료시 호출
public void disConnect() {
System.out.println("close + " + url);
}
@Override
public void afterPropertiesSet() throws Exception {
connect();
call("초기화 연결 메시지");
}
@Override
public void destroy() throws Exception {
disConnect();
}
}
➡️ InitializingBean은 afterPropertiesSet() 메서드로 초기화를 지원한다.
➡️ DisposableBean은 destory() 메서드로 소멸을 지원한다.
실행 결과
생성자 호출, url = null
NetworkClient.afterPropertiesSet
connect: http://hello-spring.dev
call: http://hello-spring.dev message = 초기화 연결 메시지
13:24:49.043 [main] DEBUG
org.springframework.context.annotation.AnnotationConfigApplicationContext -
Closing NetworkClient.destroy
close + http://hello-spring.dev
➡️ 초기화 메서드가 주입 완료 후 적절하게 호출 된 것을 확인할 수 있다.
➡️ 스프링 컨테이너의 종료가 호출되자 소멸 메서드가 호출된 것도 확인할 수 있다.
⚠️ 초기화, 소멸 인터페이스 단점
스프링 전용 인터페이스이며, 해당 코드가 스프링 전용 인터페이스에 의존하게된다.
초기화, 소멸 메서드의 이름을 변경할 수 없다.
외부 라이브러리에 적용할 수 없다.
요즘은 거의 사용하지 않는 방식이다.
2. 빈 등록 초기화, 소멸 메서드 지정
/**
* 2. 설정 정보 방법으로 bean life-cycle 초기화, 소멸에 대한 부분을 담당한다.
* 외부 라이브러리에도 적용 가능하며, 메서드 이름을 자유롭게 줄 수 있다.
* @bean에 등록할때만 설정 가능하다.
*/
public class MethodNetworkClient {
private String url;
public MethodNetworkClient() {
System.out.println("생성자 호출, url = " + url);
}
public void setUrl(String url) {
this.url = url;
}
//서비스 시작시 호출
public void connect() {
System.out.println("connect: " + url);
}
public void call(String message) {
System.out.println("call: " + url + " message = " + message);
}
//서비스 종료시 호출
public void disConnect() {
System.out.println("close + " + url);
}
public void init() {
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
public void close() {
System.out.println("NetworkClient.close");
disConnect();
}
}
설정 정보에 초기화 소멸 메서드 지정
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public MethodNetworkClient networkClient() {
MethodNetworkClient networkClient = new MethodNetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
실행 결과
생성자 호출, url = null
NetworkClient.init
connect: http://hello-spring.dev
call: http://hello-spring.dev message = 초기화 연결 메시지
13:33:10.029 [main] DEBUG
org.springframework.context.annotation.AnnotationConfigApplicationContext -
Closing NetworkClient.close
close + http://hello-spring.dev
➡️ 메서드 이름을 자유롭게 줄 수 있다.
➡️ 스프링 빈이 스프링 코드에 의존하지 않는다.
➡️ 코드가 아니라 설정 정보를 사용하기 때문에, 외부 라이브러리에도 적용할 수 있다.
종료 메서드 추론
- @Bean의 destoryMethod 속성에는 특별한 기능이 존재한다.
- 라이브러리는 대부분 close, shutdown 이라는 이름의 종료 메서드를 사용한다.
- @Bean의 destoryMethod는 기본 값이 추론 (inferred)으로 등록되어 있다.
- 이 추론 기능을 통해 close, shutdown이라는 이름의 메서드를 자동으로 호출해준다.
- 스프링 빈으로 등록하면 종료 메서드는 따로 적어주지 않아도 잘 동작한다.
- 추론 기능을 사용하기 싫으면 destoryMethod="" 처럼 빈 공백을 지정하면 된다.
3. 어노테이션 @PostConstruct, @PreDestory
/**
* 3. annotation 방법으로 bean life-cycle 초기화, 소멸에 대한 부분을 담당한다.
* spring에서 가장 권장하는 방법이다.
* 외부 라이브러리에는 적용이 불가능하다.
*/
public class AnnotationNetworkClient {
private String url;
public AnnotationNetworkClient() {
System.out.println("생성자 호출, url = " + url);
}
public void setUrl(String url) {
this.url = url;
}
//서비스 시작시 호출
public void connect() {
System.out.println("connect: " + url);
}
public void call(String message) {
System.out.println("call: " + url + " message = " + message);
}
//서비스 종료시 호출
public void disConnect() {
System.out.println("close + " + url);
}
@PostConstruct
public void init() {
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
@PreDestroy
public void close() {
System.out.println("NetworkClient.close");
disConnect();
}
}
@Configuration
static class LifeCycleConfig {
@Bean
public AnnotationNetworkClient networkClient() {
AnnotationNetworkClient networkClient = new AnnotationNetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
실행 결과
생성자 호출, url = null
NetworkClient.init
connect: http://hello-spring.dev
call: http://hello-spring.dev message = 초기화 연결 메시지
19:40:50.269 [main] DEBUG
org.springframework.context.annotation.AnnotationConfigApplicationContext -
Closing NetworkClient.close
close + http://hello-spring.dev
➡️ 최신 스프링에서 가장 권장하는 방법이다.
➡️ 어노테이션 하나만 붙이면 되므로 매우 편리하다.
➡️ 패키지를 보게되면, javax.annotation.PostConstruct이다.
스프링에 종속된 기술이 아니라, JSR-250이라는 자바 표준 기술이다.
따라서 스프링이 아닌 다른 컨테이너에서도 동작한다.
➡️ 유일한 단점은 외부 라이브러리에는 적용하지 못한다는 것이다.
외부 라이브러리를 초기화, 종료해야하면 @Bean 기능을 사용해야한다.
빈 설정 메타 정보 (BeanDefinition)
💡 스프링의 다양한 설정 형식 (Java, XML 등)은 BeanDefinition이라는 추상화 덕분에 가능하다.
쉽게 얘기해서 역할과 구현을 개념적으로 나눈것이다.
즉, 스프링 컨테이너는 메타정보를 기반으로 스프링 빈을 생성한다.
- AnnotationConfigApplicationContext는 AnnotatedBeanDefinitionReader를 사용해서
Appconfig.class를 읽고 BeanDefinition을 생성한다. - GenericXmlApplicationContext는 XmlBeanDefinitionReader를 사용해서
appConfig.xml 설정 정보를 읽고, BeanDefinition을 생성한다. - 새로운 형식의 설정 정보가 추가되면, XxxBeanDefinitionReader를 만들어서
BeanDefinition을 생성하면 된다.
BeanDefinition 정보
➡️ BeanClassName : 생성할 빈의 클래스명 (자바 설정 처럼 팩토리 역할의 빈을 사용하면 없다)
➡️ factoryBeanName: 팩토리 역할의 빈을 사용할 경우 이름, ex) appConfig
➡️ factoryMethodName : 빈을 생성할 팩토리 메서드 지정, ex) memberService
➡️ Scope : 싱글톤 (기본 값)
➡️ layzInit : 스프링 컨테이너를 생성할 때, 실제 빈을 사용할 때 까지 최대한 생성을 지연 처리 하는 여부
➡️ InitMethodName : 빈을 생성하고, 의존관계를 적용한 뒤에 호출되는 초기화 메서드 명
➡️ DestoryMethodName : 빈의 생명주기가 끝나서 제거하기 직전에 호출되는 메서드 명
➡️ Constructor arguments, Properties : 의존관계 주입에서 사용한다.
빈 설정 메타정보 확인 예제
public class BeanDefinitionTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
// GenericXmlApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
@Test
@DisplayName("빈 설정 메타정보 확인")
void findApplicationBean() {
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
System.out.println("beanDefinitionName = " + beanDefinitionName + " beanDefinition = " + beanDefinition);
}
}
}
}
OUTRO
오늘은 스프링 빈 객체에 대해서 알아봤습니다.
결국 위 내용을 이해하려면, 스프링 컨테이너에 대한 개념도 알아야하는데,
추후 포스팅에서 다룰 예정입니다.
그러면 다음 포스팅에서 뵙겠습니다.
위 포스팅 글은 김영한님의 스프링 핵심 원리 - 기본편을 참고했습니다.
스프링 핵심 원리 - 기본편 | 김영한 - 인프런
김영한 | 스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 백엔드 개발자가 되어보
www.inflearn.com
감사합니다.
'Back-End > Spring' 카테고리의 다른 글
[Spring] IOC, DI, DL 란? (0) | 2024.04.05 |
---|---|
[Spring Boot] 스프링 부트 기초와 개념 (0) | 2024.03.28 |