본문 바로가기
spring

[Spring] RequestContextHolder로 HttpServletRequest 가져오기

by moonsiri 2022. 7. 21.
728x90
반응형

HttpServletRequest를 메서드 파라미터로 선언하지 않고 가져오는 방법입니다.

request를 서비스 레이어까지 전달하지 않더라도 RequestContextHolder를 이용하면 되는데, RequestContextHolder는 Spring 프레임워크 전 구간에서 HttpServletRequest에 접근할 수 있게 도와주는 구현체입니다.

RequestContextHolder는 ThreadLocal을 사용해서 servlet이 호출되면 thread, HttpServletRequest를 key-value로 보관하고 있다가 요청을 하면 동일한 thread내에서는 어느 곳에서든 같은 HttpServletRequest를 돌려주는 역할을 합니다.

 

 

RequestContextHolder에는 HttpServletRequest를 조회하는 메서드가 두 가지 존재합니다.

1. getRequestAttributes

@Nullable
public static RequestAttributes getRequestAttributes()
Return the RequestAttributes currently bound to the thread.
Returns:
the RequestAttributes currently bound to the thread, or null if none bound

 

2. currentRequestAttributes

public static RequestAttributes currentRequestAttributes() throws IllegalStateException
Return the RequestAttributes currently bound to the thread.
Exposes the previously bound RequestAttributes instance, if any. Falls back to the current JSF FacesContext, if any.
Returns:
the RequestAttributes currently bound to the thread
Throws:
IllegalStateException - if no RequestAttributes object is bound to the current thread
See Also:
setRequestAttributes(org.springframework.web.context.request.RequestAttributes), ServletRequestAttributes, FacesRequestAttributes, FacesContext.getCurrentInstance()

 

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

public class HttpRequestUtils {

	// (1)
	public static HttpServletRequest getRequest() {
		return ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
	}
    
	// (2)
	public static HttpServletRequest getCurrentRequest() {
		return ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
	}
}

 

간단히 정리하면 getRequestAttributes()는 RequestAttributes가 없으면 Null을 반환하고, currentRequestAttributes()는 RequestAttributes가 없으면 IllegalStateException을 발생시킵니다.

 

 

+)

Spring @Async를 사용하면 쉽게 비동기 처리할 수 있습니다. 하지만 비동기 쓰레드에서는 호출한 쓰레드와 다른 쓰레드이므로 RequestContextHolder에서 HttpServletRequest를 조회하지 못합니다. 그래서 흔히 메서드 파라미터로 HttpServletRequest를 전달하여 사용하려 하는데 보통 코드가 빨리 실행되어 정상적으로 동작하는 듯 보이나, 비동기를 호출한 쓰레드가 먼저 종료되면 HttpServletRequest의 scope도 함께 종료되어 비동기 쓰레드에서 HttpServletRequest를 가져오는데 실패하게 됩니다.

@Async
public void getMethod(HttpServletRequest request) {
    System.out.println("parameter request = " + request.getMethod());
    try {
        System.out.println("RequestContextHolder.getRequestAttributes() = " + ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest().getMethod());
    } catch (NullPointerException e) {
        System.out.println("RequestContextHolder.getRequestAttributes() = NullPointerException");
    }
    try {
        System.out.println("RequestContextHolder.currentRequestAttributes() = " + ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest().getMethod());
    } catch (IllegalStateException e) {
        System.out.println("RequestContextHolder.currentRequestAttributes() = IllegalStateException");
    }

    try {
        System.out.println("###### Thread.sleep(10000)");
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("parameter request = " + request.getMethod());
    try {
        System.out.println("RequestContextHolder.getRequestAttributes() = " + ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest().getMethod());
    } catch (NullPointerException e) {
        System.out.println("RequestContextHolder.getRequestAttributes() = NullPointerException");
    }
    try {
        System.out.println("RequestContextHolder.currentRequestAttributes() = " + ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest().getMethod());
    } catch (IllegalStateException e) {
        System.out.println("RequestContextHolder.currentRequestAttributes() = IllegalStateException");
    }

}

 

비동기 호출 시 HttpServletRequest에서 값을 조회해야 한다면 비동기 호출 전에 값을 조회하여 해당 값을 전달하는 것이 좋습니다.

@ResponseBody
@PostMapping("/method")
public ResponseEntity<Object> getMethod(HttpServletRequest request) {

    asyncGetMethod(request);

    return ResponseEntity.ok(request.getMethod());
}

@Async
public void asyncGetMethod(String method) {
    System.out.println("parameter request.getMethod() = " + method);
}

728x90
반응형

댓글