Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
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
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

웹 스코프, provider 본문

인프런/[인프런] Spring 핵심원리 이해

웹 스코프, provider

Seung__ 2021. 8. 1. 14:48
  • 웹 환경에서만 동작한다
  • 웹 스코프는 스프링이 해당 스코프 종료 시점까지 관리해줌.
  • 종료 메서드 호출

 

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


 

GitHub - bsh6463/SpringCoreFunction

Contribute to bsh6463/SpringCoreFunction development by creating an account on GitHub.

github.com

 

Comments