스프링 프레임워크를 사용하여 개발할 때 서비스나 DAO 객체에 bean을 얻기 위해서는 @Autowired 또는 @Resourse 어노테이션을 사용하여, 빈을 주입받아 사용한다.
HttpServletRequest, HttpServletResponse, HttpSession과 같은 서블릿 객체는 Controller 메서드의 인자로 받아서 사용하고 필요 시 서비스 객체로 보내줘야 한다.
하지만, 이외의 유틸리티성 객체에서 데이터베이스에 접근하고자 할 때, Controller나 서비스 객체가 아닌 리스너, AOP 등에서 서비스 객체 또는 DAO 객체등의 스프링 빈을 사용해야 할 때도 있다.
이럴 경우 필요한 빈 객체 또는 서블릿 객체에 직접 접근하는 방법이 있고, 이에 대해 정리해보고자 한다.
//빈을 직접 얻기 위한 방법
WebApplicationContext context =
ContextLoader.getCurrentWebApplicationContext();
return context.getBean(beanName);
ContextLoader의 getCurrentWebApplicationContext() 메서드를 통해서 WebApplicationContext 객체를 얻는다.
ContextLoader ?
먼저 Context란 Spring Bean의 확장 버전으로 단순히 Bean을 다루는 것 외에 더 추가적인 기능을 제공한다.
ContextLoader는 ContextLoaderListener를 통해 등록된 Context를 접근할 수 있도록 하는 객체이다.
- Context에 대한 깊은 설명은 내용을 추 후에 따로 정리해서 포스팅하려 한다.
WebApplicationContext ?
WebApplicationContext는 서비스 계층, DAO 등을 포함한 웹 환경에 독립적인 빈들을 담아두는 Context
WebApplicationContext의 .getBean() 메서드로 bean 객체의 beanName을 인자로 전달하여 Spring Bean으로 등록된 Bean 객체를 가져올 수 있다.
코드
//beanName을 통해서 Bean을 얻을 수 있다.
public static Object getBean(String beanName){
return ContextLoader.getCurrentWebApplicationContext().getBean(beanName);
}
ServletRequestAttributes attr =
(ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes();
RequestContextHolder 객체로부터 ServletRequestAttributes 객체를 얻는다.
RequestContextHolder ?
RequestContextHolder는 Spring 2.X이상 버전부터 제공하는 기능으로 HttpServletRequest에 접근할 수 있도록 도와준다.
원리
- Spring은 static(정적)한 map을 하나 만들어놓고 servlet이 호출되면 key를 thread로 만들어서 제공받은 HttpServletRequest를 보관한다.
- 그리고 Servlet이 종료될 때, 해당 thread를 key로 갖는 HttpServletRequest를 map에서 제거한다.
이러한 이유로 호출된 Servlet과 동일한 thread 내에서는 어느 곳에서든 HttpServletRequest를 꺼내 쓸 수 있게된다(동일한 thread).
(실제로는 HttpServletRequest 객체 자체가 아닌 가공된 데이터지만, HttpServletRequest를 가져다 쓰듯이 사용할 수 있다.)
ServletRequestAttributes ?
RequestContextHolder 객체는 위 설명처럼 HttpServletRequest를 ThreadLocalMap을 이용해서 HttpServletRequest 인스턴스의 정보들을 바인딩 해두는데, currentRequestAttributes(), getRequestAttributes() 메서드는 바인딩된 RequestAttributes 데이터를 ServletRequestAttributes 객체로 바인딩해서 리턴한다.
currentRequestAttributes()와 getRequestAttributes()의 차이
두 메서드는 현재 thread에 바인딩된 RequestAttributes를 가져오는 것은 동일하다.
둘의 차이는
값이 없을 경우 null로 반환할지, 예외를 발생시킬지에 따라서 사용 메소드에 차이가 있다.
Servlet의 경우 Request, Session, Response 등 여러 객체를 반환할 수 있다.
//HttpServletRequest 객체 접근
public static HttpServletRequest getRequest(){
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getRequest();
}
//getAttribute(Request에서 데이터 가져오기)
public static HttpServletRequest getRequest(){
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getRequest();
}
//setAttribute(Request에 데이터 저장)
public static void setRequestAttributes(String key, Object obj){
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
attr.setAttribute(key, obj, attr.SCOPE_REQUEST);
}
//HttpSession 객체 접근
public static HttpSession getSession(){
return getRequest().getSession();
}
//getSessionAttribute(Session에서 데이터 가져오기)
public static Object getSessionAttributes(String key){
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getAttribute(key, attr.SCOPE_SESSION);
}
//setSessionAttribute(Session에 데이터 저장)
public static void setSessionAttributes(String key, Object obj){
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
attr.setAttribute(key, obj, attr.SCOPE_SESSION);
}
//HttpServletResponse 객체 얻기
public static HttpServletResponse getResponse(){
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getResponse();
}
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class RequestHandler {
public static ServletRequestAttributes getServletRequestAttributes(){
return (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
}
//Request 객체 얻기
public static HttpServletRequest getRequest(){
return getServletRequestAttributes().getRequest();
}
//Set Request Attribute
public static void setRequestAttributes(String key, Object obj){
getServletRequestAttributes().setAttribute(key, obj, ServletRequestAttributes.SCOPE_REQUEST);
}
//Get Request Attribute
public static Object getRequestAttributes(String key){
return getServletRequestAttributes().getAttribute(key, ServletRequestAttributes.SCOPE_REQUEST);
}
//Session 객체 얻기
public static HttpSession getSession(){
return getRequest().getSession();
}
//Set Session Attributes
public static void setSessionAttributes(String key, Object obj){
getServletRequestAttributes().setAttribute(key, obj, ServletRequestAttributes.SCOPE_SESSION);
}
//Get Session Attributes
public static void getSessionAttributes(String key){
getServletRequestAttributes().getAttribute(key, getServletRequestAttributes().SCOPE_SESSION);
}
//HttpServletResponse 객체 얻기
public static HttpServletResponse getResponse(){
return getServletRequestAttributes().getResponse();
}
//beanName을 통해서 Bean을 얻을 수 있다.
public static Object getBean(String beanName){
return ContextLoader.getCurrentWebApplicationContext().getBean(beanName);
}
}