有时我们需要在非Controller层如service层而不通过Controller层传参方式而获得HttpServletRequest,HttpServletResponse,通过查找到RequestContextHolder是Spring提供的可以获取HttpServletRequest的一个工具,于是我在工作中就自己封装了一个工具类如下:
public class ServletUtils {
/**
* 获取String参数
*/
public static String getParameter(String name) {
return getRequest().getParameter(name);
}
/**
* 获取request
*/
public static HttpServletRequest getRequest() {
return getRequestAttributes().getRequest();
}
/**
* 获取response
*/
public static HttpServletResponse getResponse() {
return getRequestAttributes().getResponse();
}
/**
* 获取session
*/
public static HttpSession getSession() {
return getRequest().getSession();
}
public static ServletRequestAttributes getRequestAttributes() {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
}
}
编码时没问题,但是实际调用时RequestContextHolder.getRequestAttributes()空指针异常。
对RequestContextHolder源码分析,可参考博客 https://blog.csdn.net/u012706811/article/details/53432032
解决办法:
在web.xml配置RequestContextListener监听器:
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
或者WebConfig.class中添加
/**
* RequestContextListener监听器
* @return
*/
@Bean
public RequestContextListener requestContextListenerBean() {
return new RequestContextListener();
}
原理分析
RequestContextListener实现了ServletRequestListener ,在其覆盖的requestInitialized(ServletRequestEvent requestEvent)方法中,将request最终设置到了RequestContextHolder中.
public class RequestContextListener implements ServletRequestListener {
private static final String REQUEST_ATTRIBUTES_ATTRIBUTE = RequestContextListener.class.getName() + ".REQUEST_ATTRIBUTES";
public RequestContextListener() {
}
public void requestInitialized(ServletRequestEvent requestEvent) {
if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException("Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
} else {
HttpServletRequest request = (HttpServletRequest)requestEvent.getServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
RequestContextHolder.setRequestAttributes(attributes);
}
}
配置RequestContextListener时大家可能会想到ContextLoaderListener
RequestContextListener与ContextLoaderListener区别:
ContextLoaderListener
ContextLoaderListener extends ContextLoader implements ServletContextListener。
ServletContextListener extends EventListener。
ServletContextListener只负责监听Web容器的启动和关闭的事件。
ContextLoaderListener(或ContextLoaderServlet)将Web容器与spring容器进行整合。
这是使用Spring
必须配置
的:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Spring配置文件的声明:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
如果没有显式声明,则 系统默认 在WEB-INF/applicationContext.xml。
在一个团队使用Spring的实际项目中,应该需要多个Spring的配置文件,如何使用和交叉引用的问题:
如果想装入多个配置文件,可以用逗号作分隔符,如:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext-database.xml,applicationContext.xml</param-value>
</context-param>
多个配置文件里的交叉引用可以用ref的external或bean解决,例如:
applicationContext.xml
<bean id=”userService” class=”domain.user.service.impl.UserServiceImpl”>
<property name=”dbbean”>
<ref bean=”dbBean”/>
</property>
</bean>
dbBean在applicationContext-database.xml中。
RequestContextListener
RequestContextListener implements ServletRequestListener
ServletRequestListener extends EventListener
ServletRequestListener监听HTTP请求事件,Web服务器接收的每次请求都会通知该监听器。
RequestContextListener将Spring容器与Web容器结合的更加密切。这是
可选配置
,并且后者与scope=”request”搭配使用:
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
两者区别
ContextLoaderListener(或ContextLoaderServlet)将Web容器与spring容器整合。RequestContextListener将Spring容器与Web容器结合的更加密切。
前者为必选配置,后者为可选配置,并且后者与
scope=”request”
搭配使用。