Spring - 빈 스코프

YUNU·2023년 8월 7일
0

스프링

목록 보기
20/33
post-thumbnail

🌱 Spring


🟦 빈 스코프

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

🟦 스프링이 지원하는 스코프

🔷 싱글톤
기본 스코프
스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프


🔷 프로토타입
프로토타입 빈의 생성과 의존관계까지만 관여
초기화 메서드까지 호출하고 끝, 종료 메서드 호출 ❌
매우 짧은 범위의 스코프


🔷 웹 관련 스코프
🔹 request : 웹 요청이 들어오고 나갈때까지 유지되는 스코프
🔹 session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
🔹 application : 웹의 서플릿 컨텍스트와 같은 범위로 유지되는 스코프


🔷 빈 스코프 지정 방법

🔹 컴포넌트 스캔 자동 등록
@Scope("prototype")
@Component
public class DDeo99{}
🔹 수동 등록
@Scope("prototype")
@Bean
PrototypeBean DDeo99() {
	return new DDeo99();
}

🟦 프로토타입 스코프

프로토타입 스코프를 스프링 컨테이너에 조회
➡️ 항상 새로운 인스턴스를 생성해서 반환

  1. 프로토타입 스코프의 빈을 스프링 컨테이너에 요청
  2. 스프링 컨테이너는 요청 받은 시점에 프로토타입을 빈을 생성, 의존관계 주입
  3. 스프링 컨테이너는 생성한 프로토타입 빈을 클라이언트에 반환, 관리❌
  4. 이후 스프링 컨테이너에 같은 요청이 오면 항상 새로운 프로토타입 빈 생성, 반환

스프링 컨테이너는 프로토타입 빈을 생성, 의존관계 주입, 초기화까지만 처리

스프링 컨테이너는 생성된 프로토타입 빈을 관리하지 않음

➡️ @PreDestory와 같은 종료 메서드 호출 ❌

➡️ 프로토타입 빈은 프로토타입 빈을 조회한 클라이언트가 관리해야함

➡️ 종료 메서드에 대한 호출도 클라이언트가 직접 해야함

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

싱글톤 빈은 스프링 컨테이너 생성 시점에 초기화 메서드 실행
프로토타입 빈은 스프링 컨테이너에서 빈을 조회할 때 생성, 초기화 메서드 실행

🔷 프로토타입 스코프를 싱글톤 빈과 함께 사용할 경우

프로토타입 스코프의 빈은 싱글톤 빈과 함께 사용하면 의도한 대로 동작하지 않음

스프링은 일반적으로 싱글톤 빈 사용

➡️ 싱글톤 빈과 프로토타입 빈을 함께 사용하면 싱글톤 빈이 프로토타입 빈을 사용

➡️ 싱글톤 빈은 생성 시점에만 의존관계 주입

➡️ 프로토타입 빈이 새로 생성되기는 하나 싱글톤 빈과 함께 계속 유지되어 문제 발생

➡️ 사용할 때마다 새로 생성해서 사용하는 프로토타입 빈의 목적을 이루지 못함


🔷 프로토타입 스코프 빈과 싱글톤 빈을 함께 사용할 경우의 문제를 해결하는 방법

지정한 프로토타입 빈을 컨테이너에서 대신 찾아주는 정도의 기능만 제공받을 수 있으면 프로토타입 빈과 싱글톤 빈을 함께 사용할 때의 문제를 해결할 수 있음

➡️ ObjectFactory, ObjectProvider

  • 의존관계 외부 주입 : DI
    ➡️ 스프링의 애플리케이션 컨텍스트 전체를 주입받아 스프링 컨테이너에 종속적인 코드가 됨
  • 의존관계 조회(탐색): DL(Dependency Lookup)
    ➡️ 직접 필요한 의존관계를 찾는 것

🔷 해결방법 1. ObjectProvider

ObjectFactory에 편의 기능을 추가해서 만듦

스프링 컨테이너를 통해서 대신 조회해주는 대리자

애플리케이션 컨텍스트를 통해서가 아닌 지정한 빈을 스프링 컨테이너에서 대신 찾아주는 DL 서비스를 제공


public void 메서드() {
			// getObject() : 스프링 컨테이너에서 프로토타입 빈을 찾아 반환
            PrototypeBean prototypeBean = prototypeBeanObjectProvider.getObject(); 
            //...
}

prototypeBeanProvider.getObject()를 통해서 항상 프로토타입 빈 생성

🔷 해결방법 2. JSR-330 Provider

JSR-330 자바 표준
스프링 부트 3.0 미만 : javax.inject:javax.inject:1 라이브러리 추가
스프링 부트 3.0 이상 : jakarta.inject:jakarta.inject-api:2.0.1 라이브러리 추가

<@Autowired
private Provider<PrototypeBena> provider;

public void 메서드() {
            PrototypeBean prototypeBean = prototypeBeanObjectProvider.get(); 
            //...
}

🔷 프로토타입 빈 사용 경우

매번 사용할 때마다 의존관계 주입이 완료된 새로운 객체가 필요한 경우에 사용

실무에서 웹 애플리케이션 개발할 때, 싱글톤 빈으로 대부분의 문제 해결 가능

➡️ 잘 사용 안함

 - ObjectFactory : 기능 단순, 별도의 라이브러리 필요 없음, 스프링에 의존
 - ObjectProvider : ObjectFactory 상속, 옵션 스트림 처리 등의 편의 기능 O, 별도의 라이브러리 필요 없음, 스프링에 의존
 
 - JSR-330 Provicer : get() 메서드 하나로 기능이 매우 단순, 별도의 라이브러리 필요, 자바 표준 -> 다른 컨테이너에서도 사용 가능

🟦 웹 스코프

웹 스코프는 웹 환경에서만 동작 -> web 환경이 동작하도록 라이브러리 추가해야함

프로토타입과 달리 스프링이 해당 스코프의 종료 시점까지 관리 -> 종료 메서드 호출

//web 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-web'
-참고-
웹과 관련된 부분은 컨트롤러까지만 사용
서비스 계층은 웹 기술에 종속되지 않고, 가급적 순수하게 유지하는 것이 유지보수 관점에서 좋음

🔷 웹 스코프 종류

▪️ request
: HTTP 요청이 들어와서 나갈 때까지 유지, 각각의 HTTp 요청마다 별도의 빈 인스턴스 생성 및관리

클라이언트 A가 요청을 하면 HTTP 요청이 들어와서 나갈 때까지 하나의 A 전용 스프링 빈 사용
클라이언트 B가 요청을 하면 HTTP 요청이 들어와서 나갈 때까지 하나의 B 전용 스프링 빈 사용

▪️ session : HTTP Session과 동일한 생명주기 가짐

▪️ application : ServletContext와 동일한 생명주기 가짐

▪️ websocket : 웹 소켓과 동일한 생명주기 가짐

🔷 request 스코프 빈은 스프링 애플리케이션 실행 시점에 존재 ❌

동시에 여러 HTTP 요청이 오면 어떤 요청이 남긴 로그인지 구분하기 어려움

➡️ request 스코프를 활용

✔️ 다만 스프링 애플리케이션을 실행하는 시점에 request 스코프 빈은 생성되지 않으므로
이 문제를 해결하여 사용해야 함

🔷 해결방법 1. 스코프와 Provider

ObjectProvider를 사용함으로써 ObjectProvider.getObject()를 호출하는 시점까지
request 스코브 빈을 스프링 컨테이너에 요청하는 것을 지연할 수 있음

ObjectProvider.getObject() 호출하는 시점은 HTTP 요청 진행중
➡️ request 스코프 빈 생성 OK

🔷 해결방법 2. 스코프와 프록시

proxyMode = ScopedProxyMode.TARGET_CLASS 옵션 추가

적용 대상이 인터페이스 - INTERFACES
적용 대상이 인터페이스 X - TARGET_CLASS

	@Component
	@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class 클래스 {
}

'클래스'의 가짜 프록시 클래스를 만들어두고 HTTP request와 상관 없이 가짜 프록시 클래스를 다른 빈에 미리 주입 가능

➡️ CGLIB 라이브러리가 내 클래스를 상속받은 가짜 프록시 개체를 만들어 주입

➡️ 우리가 등록한 '클래스'가 아닌 '클래스'$$EnhancerBySpringCGLIB 객체가 대신 등록

➡️ ac.getBean()을 사용해서 우리가 등록한 클래스를 조회해도 프록시 객체가 조회됨

➡️ 의존 관계 주입도 이 가짜 프록시 개체가 주입됨


가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직 존재

➡️ 가짜 프록시 객체는 내부에 진짜 '클래스'를 찾는 방법을 알고 있음

➡️ 가짜 프록시 객체는 원본 클래스를 상속받아서 만들어짐

➡️ 이 객체를 사용하는 클라이언트는 원본인지 아닌지도 모르게 동일하게 사용 가능(다형성)


가짜 프록시 개체는 실제 request scope과는 관계 없음, 싱글톤처럼 동작

어노테이션 설정 변경만으로 원본 객체를 프록시 객체로 대체 가능(다형성, DI컨테이너 의 강점)

마치 싱글톤 빈을 사용하듯이 편리하게 request scope 사용 가능
(싱글톤을 사용하는 것 같지만 동작 방식은 다름 -> 필요한 곳에만 최소화해서 사용)


✅ Provider든 Proxy든 중요한 점은 진짜 객체 조회를 필요한 시점까지 지연 처리한다는 것



인프런 스프링 핵심 원리 - 기본편 (김영한) 참조

profile
DDeo99

0개의 댓글