@Autowired HttpServletRequest 사용하기(+RequestContextHolder, ThreadLocal)
클라이언트 요청을 처리할 때 Request 객체를 다루는 경우가 있다.
일반적으로는 Controller에서 HttpServletRequest를 사용하여 객체를 꺼내오는 방법이 있다.
컨트롤러에서 가져오기
@Controller
public class MyController {
@RequestMapping("/myEndpoint")
public String handleRequest(HttpServletRequest request) {
// Request 객체 활용 // ...
return "response";
}
}
RequestContextHolder에서 가져오기
만약에 Controller 영역이 아닌 곳에서는 일반적으로 아래와 같이 사용할 수 있었다.
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
ThreadLocal에서 가져오기
private static final ThreadLocal<?> threadLocal = new ThreadLocal<>();
threadLocal.set("값");
위와 같이 request에 대한 요청별로 threadLocal에 담아두고 사용할 수도 있다.
ThreadLocal은 쓰레드 간에 각각 독립적인 값을 할당하여 쓰레드 간 경합을 방지한다.
보통은 static으로 전역으로 설정해도 값이 충돌날 위험이 없다.(It helps to think of a ThreadLocal as a HashMap where Thread.currentThread() is the key.)
그래서 요청마다 객체 생성을 할 필요 없기 때문에 주로 static으로 설정되며 내부적으로는 HashMap과 유사한 구조를 가지고 있기 때문에 충돌날 위험이 없다.
주입 받아 사용하기
여튼 Request 객체를 보존하기 위해 위와 같은 방법들을 사용할 수가 있는데 사실 ThreadLocal까지 직접 구현하는 순간 static을 사용해야해서 테스트 코드 작성도 힘들어지고 코드 복잡성이 올라간다.
스프링 3.0 이상에서는 아래와 같이 Spring Bean 처럼 관리할 수가 있다.
@Autowired
HttpServletRequest request;
HttpServletRequest는 내부적으로 RequestContextHolder를 통해 현재 요청 객체를 검색한다.
RequestContextHolder는 내부적으로 ThreadLocal을 사용하여 각 쓰레드에게 현재 요청 객체를 제공한다.
스프링에서 Filter를 다룰 때는 보통 Filter는 스프링 영역이 아닌 서블릿 영역이라 Bean으로 다루지 않는다고 생각할 수도 있지만 Bean으로서 관리될 수도 있는데 HttpServletRequest도 마찬가지로 Bean으로서 관리할 수 있다.
아래 두 개의 글을 참고한 것이다.
결론 요약
결론적으로는 RequestContextHolder나 ThreadLocal을 쓰지 않더라도 HttpServletRequest를 주입해서 사용하면된다.
HttpServletRequest나 Filter는 스프링 영역이 아니지만 Bean으로서 관리되기도 한다.