🧐강의🧐
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard
↓ ↓ 이어지는 강의 ↓ ↓
2024.01.03 - [Spring/스프링 핵심 원리 - 기본편] - [빈 스코프] 웹 스코프 request 예제 만들기
✨ 문제 상황
- 같은 HTTP 요청 시 같은 스프링 빈 반환 → 새로운 객체가 생성되지 않는 문제
· LogDemoController
➡️ ObjectProvider 코드 추가하기
@Controller
@RequiredArgsConstructor
public class LobDemoController {
...
private final ObjectProvider<MyLogger> myLoggerProvider;
@RequestMapping("log-demo")
@ResponseBody
public String logDemo(HttpServletRequest request) {
MyLogger myLogger = myLoggerProvider.getObject();
...
}
}
· LogDemoService
➡️ ObjectProvider 코드 추가하기
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myLoggerProvider;
public void logic(String id) {
MyLogger myLogger = myLoggerProvider.getObject();
...
}
}
· hello.core.CoreApplication 실행 후 웹 브라우저 접속하기
· 로그 확인하기
✔️ ObjectProvider 사용으로 ObjectProvider.getObject() 호출 시점까지 request scope 빈 생성 지연
✔️ ObjectProvider.getObject() 호출 시점 : HTTP 요청 진행 중이므로 request scope 빈 생성 정상 처리
➡️ ObjectProvider.getObject() 를 Controller 와 Service 에서 각각 호출해도, 같은 HTTP 요청이면 같은 스프링 빈 반환!
✨ 해결 방법
- CGLIB 라이브러리로 내 클래스를 상속받은 가짜 프록시 객체를 만들어 주입하기
· myLogger
✅ MyLogger 의 가짜 프록시 클래스 생성 후, HTTP request 와 상관 없이 가짜 프록시 클래스를 다른 빈에 미리 주입 가능
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger { ... }
➡️ 적용 대상이 인터페이스일 경우 : proxyMode = ScopedProxyMode.INTERFACES
➡️ 적용 대상이 인터페이스가 아닌 클래스일 경우 : proxyMode = ScopedProxyMode.TARGET_CLASS
· LogDemoController
@Controller
@RequiredArgsConstructor
public class LobDemoController {
...
private final MyLogger myLogger;
@RequestMapping("log-demo")
@ResponseBody
public String logDemo(HttpServletRequest request) {
String requestURL = request.getRequestURL().toString();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
· LogDemoService
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger myLogger;
public void logic(String id) {
myLogger.log("service id = " + id);
}
}
· 동작 원리
- @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) 설정 시, 스프링 컨테이너는 CGLIB 라이브러리를 사용하여 MyLogger 를 상속받은 가짜 프록시 객체 생성 (바이트코드 조작)
→ 확인 시 MyLogger$$EnhancerBySpringCGLIB 클래스로 만들어진 객체가 대신 등록되어 있음 + 의존 관계 주입
(MyLogger 대신 등록된 가짜 프록시 객체) - 클라이언트가 myLogger.log() 호출 시, 가짜 프록시 객체 메서드 호출
- 가짜 프록시 객체가 request scope 의 진짜 myLogger.log() 호출
→ 가짜 프록시 객체는 원본 클래스를 상속받았기 때문에, 진짜 빈을 요청하는 위임 로직이 들어있음
→ 클라이언트 입장에서는 동일하게 사용 가능 (다형성)
동작 정리
- CGLIB 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체 생성 후 의존 관계 주입
- 실제 요청이 들어오면, 가짜 프록시 객체는 내부에서 실제 빈을 요청하는 위임 로직 실행
- 가짜 프록시 객체는 싱글톤처럼 동작 (실제 request scope 과는 상관X)
특징 정리
- 클라이언트는 싱글톤 빈을 사용하듯 request scope 사용 가능
- ⭐ 진짜 조회 객체를 필요한 시점까지 지연 처리
- 애노테이션 설정 변경만으로 원본 객체를 프록시 객체로 대체 가능 (다형성 & DI 컨테이너의 강점)
- 웹 스코프가 아니더라도 프록시 사용 가능
주의점
- 마치 싱글톤을 사용하는 것 같지만, 실제로는 동작 방식이 다르기 때문에 주의해서 사용
- 무분별한 사용 시, 유지보수가 어려워져 필요한 곳에 최소화하여 사용
'Spring > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
[빈 스코프] 싱글톤 & 프로토타입 빈 함께 사용하기 : Provider (0) | 2024.01.04 |
---|---|
[빈 스코프] 웹 스코프 request 예제 만들기 (0) | 2024.01.03 |
[빈 스코프] 싱글톤 스코프 빈 & 프로토타입 스코프 빈 조회하기 (0) | 2024.01.02 |
[빈 스코프] 스코프 종류(싱글톤, 프로토타입, 웹) & 지정 방법 (0) | 2024.01.01 |
[빈 생명주기 콜백] 스프링 빈 생명주기 콜백 지원하는 방법 3가지 - 인터페이스, 설정 정보, 애노테이션 (0) | 2023.12.29 |