Struts2 中有一个类,叫做ServletActionContext,它是Action的上下文,Struts2中的Action就通过它来访问原生Servlet的API。该类中封装了所有用于访问Servlet的方法:
1.static HttpServletResponse getResponse():获得HttpServletResponse对象;
2.static ServletContext getServletContext():获取web应用的上下文对象;
3.Map getSession():返回一个Map对象,该对象模拟了HttpSession实例;
4.Map getParameters():获得所有的请求参数,类似于HttpServletRequest的getPrameterMap();
5.static PageContext getPageContext():返回PageContext对象;
等等。。。
Struts2中访问ServletAPI 的关键在于ActionContext, ActionContext是一个Map类型的Class。它的生命周期取决于与之对应的request对象,它的每一次创建都是由于一个HTTPServletRequest创建的线程所启动的,请求完成之后销毁。
如何获得当前请求的ActionContext:将创建的ActionContext 绑定到当前线程,当然这不需要我们进行设计,这是Struts2设计好的,因而可以通过ThreadLocal获得。
下面详解访问ServletAPI的三种方式:
1.ActionContext
ActionContext ctx=ActionContext.getContext();这里我们是有必要查看该方法的源码
public static ActionContext getContext() { return (ActionContext)actionContext.get(); }我们可以看到通过上述静态方法,获得了ActionContext对象
这里又指向了某个静态属性
static ThreadLocal<ActionContext> actionContext = new ThreadLocal();
补充一些ThreadLocal的知识点,ThreadLocal的翻译,我个人觉得比较合适的应该是“ 线程局部变量”,使得各线程能够各自保持一个独立的对象,它并非通过ThreadLocal.set()来实现,而是使用每个线程中的new方法创建对象。这个actionContext对象,就是一个典型。
接下来,假设我们要获取一个原生Session,那么
Map<String, Object> session = ctx.getSession();需要记住,ActionContext本质的数据结构是Map类型,这意味着,getSession()的实现方式极有可能类似于Map中获取值的方式,源码如下:
public Map<String, Object> getSession() { return (Map)this.get("com.opensymphony.xwork2.ActionContext.session"); }有意思的是,获取Request域对象的方法在形式上和原始的Map获取值的方法十分类似,
//获得request对象 Map<String, Object> request = (Map<String,Object>)ctx.get("request");由于actionContext对象和request的生命周期一致,所以struts2并不推荐将属性放置到request中,直接放入actionContext似乎更为方便,如下:
ctx.put("attrName","attrValue");在StrutsPreparedAndExcuteFilter中
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; try { if (this.excludedPatterns != null && this.prepare.isUrlExcluded(request, this.excludedPatterns)) { chain.doFilter(request, response); } else { this.prepare.setEncodingAndLocale(request, response); this.prepare.createActionContext(request, response); this.prepare.assignDispatcherToThread();
//此处对原生request对象进行了包装 request = this.prepare.wrapRequest(request); ActionMapping mapping = this.prepare.findActionMapping(request, response, true); if (mapping == null) { boolean handled = this.execute.executeStaticResourceRequest(request, response); if (!handled) { chain.doFilter(request, response); } } else { this.execute.executeAction(request, response, mapping); } } } finally { this.prepare.cleanupRequest(request); } }
原生HttpServletRequest在这个Filter中被包装,以改变该域对象取值的位置,以便其搜寻ActionContext中的对象值,因而在取值时,和原来没有区别。
1.ServletContext
依然大致围观一下源码:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.apache.struts2; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.util.ValueStack; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.jsp.PageContext; import org.apache.struts2.dispatcher.mapper.ActionMapping; //继承自ActionContext public class ServletActionContext extends ActionContext implements StrutsStatics { private static final long serialVersionUID = -666854718275106687L; public static final String STRUTS_VALUESTACK_KEY = "struts.valueStack"; public static final String ACTION_MAPPING = "struts.actionMapping"; private ServletActionContext(Map context) { super(context); } public static ActionContext getActionContext(HttpServletRequest req) { ValueStack vs = getValueStack(req); return vs != null ? new ActionContext(vs.getContext()) : null; } public static ValueStack getValueStack(HttpServletRequest req) { return (ValueStack)req.getAttribute("struts.valueStack"); } public static ActionMapping getActionMapping() { return (ActionMapping)ActionContext.getContext().get("struts.actionMapping"); } public static PageContext getPageContext() { return (PageContext)ActionContext.getContext().get("com.opensymphony.xwork2.dispatcher.PageContext"); } public static void setRequest(HttpServletRequest request) { ActionContext.getContext().put("com.opensymphony.xwork2.dispatcher.HttpServletRequest", request); } public static HttpServletRequest getRequest() { return (HttpServletRequest)ActionContext.getContext().get("com.opensymphony.xwork2.dispatcher.HttpServletRequest"); } public static void setResponse(HttpServletResponse response) { ActionContext.getContext().put("com.opensymphony.xwork2.dispatcher.HttpServletResponse", response); } public static HttpServletResponse getResponse() { return (HttpServletResponse)ActionContext.getContext().get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"); } public static ServletContext getServletContext() { return (ServletContext)ActionContext.getContext().get("com.opensymphony.xwork2.dispatcher.ServletContext"); } public static void setServletContext(ServletContext servletContext) { ActionContext.getContext().put("com.opensymphony.xwork2.dispatcher.ServletContext", servletContext); } }我们发现,首先它继承ActionContext,其次它本质上依然是通过调用ActionContext的方法来对ServletAPI进行访问,需要注意,它没有实现getSession方法,因为在获得原生request对象之后,调用request的getSession()方法即可。
3.实现接口
关于Servlet原生API的接口有ServletRequestAware、ServletResponseAware等等,通过实现接口,之后实现其接口方法,即可获取ServletAPI。
接口的功能实际上式通过Interceptor实现的,在struts-default.xml的配置中默认配置了20多个拦截器,其中有一个拦截器名为servletconfig,它的实现类为
package org.apache.struts2.interceptor
public String intercept(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); ActionContext context = invocation.getInvocationContext(); HttpServletRequest request; if (action instanceof ServletRequestAware) { request = (HttpServletRequest)context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest"); ((ServletRequestAware)action).setServletRequest(request); }通过ActionInvocation获得ActionContext然后去获取Servlet原生request,其他如response等等也是类似原理。