Request Scope Bean과 Proxy

Hansu Kim·2022년 2월 21일
0

Spring boot

목록 보기
9/10

웹 스코프는 아래와 같은 종류가 있다.

  • request: HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고, 관리된다.
  • session: HTTP Session과 동일한 생명주기를 가지는 스코프
  • application: 서블릿 컨텍스트( ServletContext )와 동일한 생명주기를 가지는 스코프
  • websocket: 웹 소켓과 동일한 생명주기를 가지는 스코프

Servlet이란? ServletContext란?
https://jeong-pro.tistory.com/222

Request Scope

Request Scope Bean은 HTTP Request 당 하나씩 생성되고, HTTP 요청이 끝나는 시점에 소멸된다.
Bean마다 초기화 콜백 메소드로 uuid를 생성하여 사용하면 다른 HTTP 요청과 구분할 수 있다.

@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 + "] " + requestURL + "] request scope bean create:" + this);
    }
    @PreDestroy
    public void close(){
        System.out.println("");
        System.out.println("[" + uuid + "] " + requestURL + "] request scope bean close:" + this);
    }
}

Request Scope Bean은 Provider와 함께 사용되어야 한다.
MyLogger 클래스가 그대로 Controller에 그대로 DI 된다면, 컴파일 시점에서 해당 빈은 생성되지 않기 때문에 MyLogger not initialized 라는 오류 메시지가 출력되게 된다.

그에 따라 해당 문제를 방지하기 위해서 Request Scope Bean은 Provider와 함께 사용되어야 한다.

@Controller
@RequiredArgsConstructor
public class LogDemoController {

    private final LogDemoService logDemoService;
    private final ObjectProvider<MyLogger> myLoggerProvider;

    @RequestMapping("log-demo")
    @ResponseBody
    public String logDemo(HttpServletRequest request){
        String requestURL = request.getRequestURL().toString();
        MyLogger myLogger = myLoggerProvider.getObject();
        myLogger.setRequestURL(requestURL);

        myLogger.log("controller test");
        logDemoService.logic("testID");
        return "OK";
    }
}
@Service
@RequiredArgsConstructor
public class LogDemoService {
    private final ObjectProvider<MyLogger> myLoggerProvider;
    public void logic(String testID) {
        MyLogger myLogger = myLoggerProvider.getObject();
        myLogger.log("service id = " + testID);
    }
}

Proxy를 사용한 프록시 객체 주입

MyLogger 클래스 예제에서 Provider를 사용하지 않는다면 컴파일 시점의 DI 과정에서 해당 객체가 생성되지 않았기 때문에 컴파일 에러가 발생했다.

하지만 MyLogger 클래스를 Proxy를 사용하여 정의해놓으면, CGLIB가 동작하여 MyLogger의 가짜 프록시 객체를 'myLogger"라는 이름으로 컨테이너에 등록해놓는다.

그리고, DI에서도 가짜 프록시 객체를 활용하여 동작하게 된다.

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

위와 같이 프록시 모드를 사용하여 가짜 객체를 활용할 경우, 진짜 MyLogger가 생성되는 시점을 컴파일 시점이 아니라, request가 발생하는 시점까지 미룰 수 있다.

즉, 해당 객체는 프록시 객체로 빈에 등록되고, 실제 Request가 발생했을 때에만 프록시 객체에서 실제 객체를 호출하여 로직이 수행되게 된다. 이를 통해 진짜 객체 조회를 꼭 필요한 시점까지 지연처리 할 수 있게 된다는 장점이 있다.

참고 컨텐츠 - 인프런 '스프링 핵심 원리 - 기본편'

0개의 댓글