Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Proxy
- 스프링 핵심 원리
- Thymeleaf
- 김영한
- Android
- JPQL
- springdatajpa
- Greedy
- db
- Servlet
- transaction
- pointcut
- jpa
- QueryDSL
- 자바
- spring
- 인프런
- 그리디
- AOP
- SpringBoot
- 백준
- Spring Boot
- Exception
- 스프링 핵심 기능
- java
- 스프링
- http
- kotlin
- JDBC
- 알고리즘
Archives
- Today
- Total
개발자되기 프로젝트
웹 스코프, provider 본문
- 웹 환경에서만 동작한다
- 웹 스코프는 스프링이 해당 스코프 종료 시점까지 관리해줌.
- 종료 메서드 호출
1.웹 스코프 종류
- requset : HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프,
각각의 HTTP 요청 마다 별도의 빈 인스턴스 생성되고, 관리된다! - session : HTTP session과 동일한 생명주기를 가지는 스코프
- application : 서블릿 컨텍스트(serveltContext)와 동일한 생명주기 가지는 스코프
- websocket : 웹 소켓과 동일한 생명주기를 가지는 스코프
2. Request 스코프
- 각 HTTP 요청에서만 사용되는 bean을 나타냄?
- 즉 client에 따라 전용 bean이 생성됨.
- client에 의해 요청이 들어오면 해당 클라이언트 전용 빈을 생성하고
스프링 컨테이너에서 request가 종료 될 때 까지 해당 빈 관리.
해당 client의 요청이 종료되면 destroy실행됨. - 스프링 컨테이너에 요청 시 빈 생성됨.
3. 웹 환경 추가
- 웹 스코프는 웹 환경에서만 동작함.
- 라이브러리 추가하자.
implementation 'org.springframework.boot:spring-boot-starter-web'
(스프링 부트는 내장 Tomcat 서버를 활용해 웹 서버롸 스프링을 실행시킴)
- 스프링 부트는 web라이브러리가 없으면 AnnotationConfigApplicationContext를 기반으로
애플리케이션 구동함. - 웹 라이브러리가 추가되면 기타 설정 및 환경이 필요함.
- 따라서 AnnotationConfigServletWebServerApplicationContext로 변경됨.. 와 길다
4. WebScope 예제
- 여러 HTTP 요청이 오면 정확히 어떤 요청이 남긴 로그인지 구분하기 어려워!
- 이럴때 사용하기 좋은게 request 스코프이다.
- 공통 포멧 : [UUID][requestURL]{message}
[d06b992f...][http://localhost:8080/log-demo] controller test - UUID?? : global하게 절~~~대로 겹치치 않는 identifier 생성함.
A class that represents an immutable universally unique identifier (UUID).
5. MyLogger class
@Component
@Scope(value = "request")
public class MyLogger {
private String uuid;
private String requestURL;
public void setRequestURL(String requestURL) {
this.requestURL = requestURL;
}
public void log(String message){
System.out.println("[" + uuid + "]" + "[" + requestURL + "] " + message);
}
@PostConstruct
public void init(){
uuid = UUID.randomUUID().toString();
System.out.println("[" + uuid + "]" + " request scope bean created : " + this); //참조값.
}
@PreDestroy
public void close(){
System.out.println("[" + uuid + "]" + " request scope bean closed : " + this); //참조값.
}
}
- @Scope("request")로 지정했기 때문에 HTTP Request마다 빈이 매 번 생성되고, HTTP 요청 종료시 소멸됨.
- 빈 생성시 @PostConstruct를 통해 초기화 메서드 진행, uuid 생성.
- 즉 인스턴스가 매 요청마다 생성되기 때문에 uuid는 각 인스턴스마다 달라짐.
- 빈 소멸 시점에 @PreDestroy를 통해 종료 메세지 출력
6. LogDemoService class
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger myLogger;
public void logic(String id) {
myLogger.log("service id = " + id);
}
}
- 웹과 관련된 부분은 controller까지만! 사용하자.
- 서비스 계층은 웹 기술에 종속되지 않고, 최대한 순수하게 유지하자.
7. LogDemoController class
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
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";
}
}
- 음? 스프링 컨테이너가 시작되고 controller를 빈으로 등록하는 시점을 생각해 보자.
- logdemoController 빈이 생성이 되려면 myLogger 빈을 주입 받아야 한다.
- 하지만 Mylogger는 Request가 있어야 생성 할 수 있다.
- 즉, 의존성 주입이 불가능한데...?
- 일 단 실행시키면 아래 와 같은 예외가 발생한다.
.ScopeNotActiveException: Error creating bean with name 'myLogger': Scope 'request' is not active for the current thread
- 즉 이 문제를 해결하기 위해서는 주입 받는 시점을 myLogger 생성 시점으로 미뤄야함!
- provider를 쓰면 해결됨! 어떻게..?
8. 스코프와 Provider
- ObjectProvider를 활용하여 문제를 해결해보자.
- controller 빈이 생성될 때 myLogger를 주입 받는 것이 아니라 provider를 주입을 받자.
- 이후 Requsest가 들어왔을 때 provider를 통해 스프링 컨테이너에 요청해서 myLogger빈을 가져오자.
- 이 때 myLogger빈이 생성된다.
MyLogger myLogger = myLoggerProvider.getObject();
- 그러면은 이전에 발생한 controller 빈 생성시에 myLogger가 생성되지 않은 문제가 해결된다.
- Service로직도 마찬가지로 MyLogger를 주입 받기 때문에 같이 변경해주자.
- LogDemoController
@Controller @RequiredArgsConstructor public class LogDemoController { private final LogDemoService logDemoService; private final ObjectProvider<MyLogger> myLoggerProvider; @RequestMapping("log-demo") @ResponseBody public String logDemo(HttpServletRequest request){ MyLogger myLogger = myLoggerProvider.getObject(); 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 ObjectProvider<MyLogger> myLoggerProvider; public void logic(String id) { MyLogger myLogger = myLoggerProvider.getObject(); myLogger.log("service id = " + id); } }
- 에러가 안나고 스프링이 잘 뜬다!!! ㅗㅜㅑ
- Request 결과
[f501c353-1d93-448d-998a-4dae04aed62f] request scope bean created : hello.core.common.MyLogger@1156abb6 [f501c353-1d93-448d-998a-4dae04aed62f][http://localhost:8080/log-demo] controller test [f501c353-1d93-448d-998a-4dae04aed62f][http://localhost:8080/log-demo] service id = testId [f501c353-1d93-448d-998a-4dae04aed62f] request scope bean closed : hello.core.common.MyLogger@1156abb6
- request가 들어왔을 대 request 빈이 생성이 되고, request 종료 시 종료 메서드가 실행되었다.\
- request를 여러번 날려볼까
-
[f501c353-1d93-448d-998a-4dae04aed62f] request scope bean created : hello.core.common.MyLogger@1156abb6 [f501c353-1d93-448d-998a-4dae04aed62f][http://localhost:8080/log-demo] controller test [f501c353-1d93-448d-998a-4dae04aed62f][http://localhost:8080/log-demo] service id = testId [f501c353-1d93-448d-998a-4dae04aed62f] request scope bean closed : hello.core.common.MyLogger@1156abb6 [de7bac92-0476-47df-936e-fa6705a8557c] request scope bean created : hello.core.common.MyLogger@33d0f297 [de7bac92-0476-47df-936e-fa6705a8557c][http://localhost:8080/log-demo] controller test [de7bac92-0476-47df-936e-fa6705a8557c][http://localhost:8080/log-demo] service id = testId [de7bac92-0476-47df-936e-fa6705a8557c] request scope bean closed : hello.core.common.MyLogger@33d0f297 [86830c54-f27b-41bc-a92d-41417050a4fa] request scope bean created : hello.core.common.MyLogger@7fa82176 [86830c54-f27b-41bc-a92d-41417050a4fa][http://localhost:8080/log-demo] controller test [86830c54-f27b-41bc-a92d-41417050a4fa][http://localhost:8080/log-demo] service id = testId [86830c54-f27b-41bc-a92d-41417050a4fa] request scope bean closed : hello.core.common.MyLogger@7fa82176 [2b9f6058-02cf-4ffd-ae71-29c2059de1e9] request scope bean created : hello.core.common.MyLogger@221a890d [2b9f6058-02cf-4ffd-ae71-29c2059de1e9][http://localhost:8080/log-demo] controller test [2b9f6058-02cf-4ffd-ae71-29c2059de1e9][http://localhost:8080/log-demo] service id = testId [2b9f6058-02cf-4ffd-ae71-29c2059de1e9] request scope bean closed : hello.core.common.MyLogger@221a890d [4ce2c1e6-ec08-4549-8f8a-b8c1f4f296ae] request scope bean created : hello.core.common.MyLogger@3be5f73e [4ce2c1e6-ec08-4549-8f8a-b8c1f4f296ae][http://localhost:8080/log-demo] controller test [4ce2c1e6-ec08-4549-8f8a-b8c1f4f296ae][http://localhost:8080/log-demo] service id = testId [4ce2c1e6-ec08-4549-8f8a-b8c1f4f296ae] request scope bean closed : hello.core.common.MyLogger@3be5f73e
9. 정리
- ObjectProvider덕분에 ObjctProvider.getObject()를 호출하는 시점까지
- 스프링 컨테이너에 요청하는 것을 지연할 수 있었다.
- getObject()를 호출하는 시점에는 HTTP요청이 진행 중 이므로, request scope 빈의 생성이 정상적으로 처리됨!
- ObjctProvider.getObject()을 controller, service에서 각각 호출할 때! 같은 HTTP Request면
같은 빈을 반환한다. - 하지만 더 편한 방법이 있따 --> 프록시
10. GitHub : 210801 WebScope
'인프런 > [인프런] Spring 핵심원리 이해' 카테고리의 다른 글
애플리케이션 아키텍쳐 (0) | 2021.08.06 |
---|---|
웹 스코프, 프록시 (0) | 2021.08.01 |
Provider, ObjectProvider (0) | 2021.08.01 |
프로토타입 빈과 싱글톤 빈을 같이 사용하면? (0) | 2021.07.31 |
빈 스코프?? (0) | 2021.07.30 |
Comments