GDG on Campus Backend-Spring 스터디 WIL
Week 3 - 커스텀 스코프와 빈 생명주기 콜백
캐시
여러 요청이 있을 때마다 새로 빈 인스턴스를 생성하지 않고 이미 생성된 인스턴스를 재사용할 수 있도록 저장하는 역할을 하는 저장 공간
getBean()
메소드를 호출하는데, 이때 캐시된 인스턴스를 가져오는 싱글톤과 달리 프로토타입 스코프로 정의된 빈은 매번 새로운 객체를 생성싱글톤 빈 안에 프로토타입 빈이 주입된 경우
싱글톤 빈 내부에 프로토타입 빈이 주입되면, 프로토타입 빈은 단 한 번만 생성된다. 즉, 사용 시마다 새로운 인스턴스가 생성되는 것이 아니라, 싱글톤 빈이 생성될 때 처음 한 번만 생성된 프로토타입 빈이 계속 사용된다.
해결 방법
1. ApplicationContext
를 주입받아, getBean()
을 통해 직접 요청
2. ObjectProvider
타입으로 선언, getObject()
를 통해 프로토타입 빈 return
이렇게 하면 매번 새로운 프로토타입 빈이 생성된다.
request, session, application, websocket 스코프는 web-aware Spring ApplicationContext 구현체(XmlWebApplicationContext 등)를 사용할 때만 쓸 수 있다.
Spring Web MVC 에서 사용할 경우, DispatcherServlet
이 요청과 관련된 정보들(request 객체, response 객체, session 정보 등) 을 전역적으로 사용할 수 있게 해주기 때문에 추가 설정이 필요 없다.
Web-aware ApplicationContext
HTTP 요청이나 세션, 서블릿 같은 웹 요소와 상호작용할 수 있도록 설계된 웹 전용 기능을 가진
ApplicationContext
DispatcherServlet
HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러(Front Controller)
@RequestScope
@Component
public class LoginAction {
// ...
}
LoginAction
빈에 대한 새로운 인스턴스를 만들어 request를 처리@SessionScope
@Component
public class UserPreferences {
// ...
}
ServletContext
레벨에서 유일한 인스턴스를 생성한다.@ApplicationScope
@Component
public class AppPreferences {
// ...
}
@WebSocketScope
어노테이션은 Spring 의 WebSocket 구현체인 Spring Websocket에서만 사용할 수 있다.@Component
@Scope(scopeName ="websocket", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class MyWebSocketHandler {
// ...
}
스프링 IoC 컨테이너 생성 -> 빈 생성 -> 의존성 주입 -> 초기화 콜백(
init()
) -> 빈 사용 -> 소멸 콜백(destroy()
)
콜백(callback)
- 특정 작업이나 이벤트가 발생했을 때 호출되는 함수나 메서드
- 콜백함수를 통해 특정 시점에서 실행할 동작을 미리 정의하고 원하는 타이밍에 실행할 수 있다.
- 스프링은 스프링 빈이 생성되고 소멸될 때, 초기화 작업과 종료 작업을 수행할수 있도록 초기화 콜백과 소멸전 콜백을 지원한다.
빈의 생성과 초기화의 분리
- 생성자: 객체가 생성될 때, 파라미터를 받아 메모리를 할당하여 객체 생성
- 초기화: 생성된 객체의 값들을 활용해서 외부 연결 설정이나 데이터 로드 등 무거운 동작 수행
.
객체의 생성과 초기화는 분리되는 것이 좋다. 초기화 작업이 단순한 경우에는 생성자에서 처리할 수 있지만, 일반적으로 초기화 작업은 무겁기 때문에 생성자 DI 주입 과정과 초기화 콜백을 분리하는 것이 유지보수에 유리하다.
- 인터페이스
- 설정 파일
- 어노테이션
InitializingBean
과 DisposableBean
인터페이스를 구현하여 초기화 및 소멸 시 필요한 작업을 정의할 수 있다.
public class ExampleBean implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() {
// 초기화 콜백
}
@Override
public void destroy() {
// 소멸 콜백
}
}
InitalizingBean
인터페이스afterPropertiesSet()
메소드로 초기화 지원 (의존관계 주입이 끝난 후 초기화 진행)DisposableBean
인터페이스destory()
메소드로 소멸 지원단점 |
---|
- Spring 전용 인터페이스로, Spring에 의존 - 인터페이스의 메서드를 오버라이드하여 메서드명 변경 불가 - 소스코드를 수정할 수 없는 외부 라이브러리에는 적용 불가 |
Configuration 클래스에서 @Bean
어노테이션을 사용하여 빈의 초기화 메서드와 소멸 메서드를 명시적으로 설정할 수 있다.
외부 라이브러리에서 초기화와 종료를 해야 할 경우 권장된다.
@Configuration
class LifeCycleConfig {
@Bean(initMethod = "initialize", destroyMethod = "close")
public ExampleBean exampleBean() {
return new ExampleBean();
}
}
@Bean
의 destroyMethod
옵션
@Bean(initMethod = "initialize")
이렇게 destroyMethod를 따로 지정하지 않아도 close
, shutdown
이라는 이름의 메소드를 종료 메소드라고 자동 인식해 호출한다.@Bean(initMethod = "initialize", destroyMethod="")
처럼 설정하면 된다.장점 | 단점 |
---|---|
- 메서드명 직접 지정 가능 - Spring 의존성 없음 - 외부 라이브러리에도 적용 가능 | - Bean 지정 시 initMethod 와 destroyMethod 를 직접 설정해야 하여 번거로움 |
@PostConstruct
와 @PreDestroy
어노테이션을 사용하여 초기화 및 소멸 작업을 처리할 수 있다.
최신 스프링에서 가장 권장하는 방법이다.
public class NetworkClient {
@PostConstruct
public void init() {
System.out.println("NetworkClient.init");
// 초기화 작업
}
@PreDestroy
public void close() {
System.out.println("NetworkClient.close");
// 소멸 작업
}
}
장점 | 단점 |
---|---|
- 어노테이션만으로 설정 가능해 편리 - 표준 자바 어노테이션으로, Spring 이외의 컨테이너에서도 동작 | - 커스터마이징이 불가능한 외부 라이브러리에 적용 불가 |