背景:项目中使用的框架是Struts2,为了开发效率等因素,添加SpringMVC支持。
问题解析:
Struts2获取session方法:通过Action上下文ActionContext获取;
Action上下文ActionContext详细介绍:http://blog.csdn.net/john2522/article/details/7436599
SpringMVC如何兼容Struts2,如何统一获取session取值,如何保证SpringMVC和Struts2取到的session唯一。
解决方法:
解决此类问题需要先找两个框架共性,即http请求都是基于servlet线程实现的,单次请求,从开始到业务处理结束,其实都是在一个线程中实现。
web请求图如下图:
所以问题解决的方法就是从请求线程入手,只要能保证每次请求的HttpServletRequest、HttpServletResponse取到的值一样即可。
故很容易想到使用ThreadLocal线程变量,为保证SpringMVC取到的session唯一,在SpringMVC请求和Struts2请求都加上一层过滤器(基于请求url正则匹配:SpringMVC为**.do格式,Struts2为**.action格式),将取到的含有session的请求HttpServletRequest放到置空的线程变量中,每次请求结束将线程变量置空。进而实现SpringMVC和Struts2兼容。
相关代码如下:
TheadLocal管理类:
public class ThreadLocalManager
{
/** The Constant requestLocal. */
private static final ThreadLocal<HttpServletRequest> REQUEST_LOCAL = new ThreadLocal<HttpServletRequest>();
/** The Constant responseLocal. */
private static final ThreadLocal<HttpServletResponse> RESPONSE_LOCAL = new ThreadLocal<HttpServletResponse>();
/**
* set httpServletRequst
* @param request
*/
public static void setHttpServletRequest(HttpServletRequest request)
{
REQUEST_LOCAL.set(request);
}
public static HttpServletRequest getHttpServletRequest()
{
return REQUEST_LOCAL.get();
}
/**
* set httpServletResponse
* @param request
*/
public static void setHttpServletResponse(HttpServletResponse response)
{
RESPONSE_LOCAL.set(response);
}
/**
* get httpServletResponse
* @return
*/
public static HttpServletResponse getHttpServletResponse()
{
return RESPONSE_LOCAL.get();
}
/**
* 清理需要清理的threadlocal
*/
public static void clearAllThreadLocalIfNeeded()
{
RESPONSE_LOCAL.remove();
REQUEST_LOCAL.remove();
}
}
SpringMVC拦截器:
1、在请求线程开始前线程变量为空,设置线程变量值;
2、处理相关业务;
3、结束设置线程变量为空。
public class SpringMvcFilter extends DispatcherServlet
{
private static final long serialVersionUID = -307292166973012590L;
protected static Logger logger = LoggerFactory.getLogger(SpringMvcFilter.class);
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception
{
logger.info("mvc....");
// threadlocal 清理,防止异常中断导致的脏数据
ThreadLocalManager.clearAllThreadLocalIfNeeded();
logger.info("------------start-------------");
// 主要为了适配老代码里很多地方调用struts2filter里的静态方法,防止service取不到值
ThreadLocalManager.setHttpServletRequest(request);
ThreadLocalManager.setHttpServletResponse(response);
long startTime = System.currentTimeMillis();
try
{
HttpSession session = request.getSession();
if (null == session.getAttribute("user"))
{
logger.warn("未取到用户session url:{}", request.getRequestURL());
if (request.getHeader("x-requested-with") != null
&& request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest"))
{
responseJson(response, "未取到用户session!");
return;
}
try
{
response.sendRedirect("/index.action");
return;
}
catch (IOException e1)
{
e1.printStackTrace();
}
return;
}
super.doService(request, response);
}
catch (Exception e)
{
logger.info(e.getMessage());
}
finally
{
logger.info("------------end-------------");
ThreadLocalManager.clearAllThreadLocalIfNeeded();
long endTime = System.currentTimeMillis();
logger.info("SpringMvcFilter request:[url]:" + request.getRequestURI() + ",[cost]:" + (endTime - startTime));
}
}
private static void responseJson(HttpServletResponse response, String content)
{
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
response.setHeader("Cache-Control", "no-cache");
PrintWriter pw = null;
try
{
pw = response.getWriter();
pw.write(content);
pw.flush();
}
catch (IOException e)
{
logger.error("响应结果失败", e);
}
finally
{
if (pw != null)
{
pw.close();
}
}
}
}
同理Struts2拦截器类:
public class Struts2Filter extends StrutsPrepareAndExecuteFilter
{
/** 日志. */
protected static Logger logger = LoggerFactory.getLogger(Struts2Filter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException
{
logger.info("struts2....");
// 清理threadlocal,防止数据错乱
ThreadLocalManager.clearAllThreadLocalIfNeeded();
logger.info("------------start-------------");
request.setCharacterEncoding("UTF-8");
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpRsp = (HttpServletResponse) response;
ThreadLocalManager.setHttpServletRequest(httpReq);
ThreadLocalManager.setHttpServletResponse(httpRsp);
try
{
long startTime = System.currentTimeMillis();
super.doFilter(httpReq, httpRsp, chain);
}
finally
{
logger.info("------------end-------------");
ThreadLocalManager.clearAllThreadLocalIfNeeded();
}
}
/**
* 获取当前线程的response.
*
* @return the responseLocal
*/
public static HttpServletResponse getResponse()
{
return ThreadLocalManager.getHttpServletResponse();
// return (HttpServletResponse) RESPONSE_LOCAL.get();
}
public static HttpSession getSession(boolean create)
{
HttpServletRequest req = ThreadLocalManager.getHttpServletRequest();
if (req != null)
{
return req.getSession(create);
}
else
{
logger.info("未取到request");
}
return null;
}
}
在web.xml中配置拦截器:
<filter>
<filter-name>struts2</filter-name>
<filter-class>
com.**.**.core.filter.Struts2Filter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<servlet>
<servlet-name>dispatcher</servlet-name>
<!-- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>-->
<servlet-class>com.**.**.core.filter.SpringMvcFilter</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/spring/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>