首页 > 代码库 > Struts Actioncontext 和ServletConfigInterceptor的原理分析
Struts Actioncontext 和ServletConfigInterceptor的原理分析
最近你在做网上商城的项目时对Struts的Actioncontext的原理产生的疑问,于是通过查找资料最后有了一定理解,在此写下随笔为自己的思路做整理。
web.xml代码:
1 <filter>2 <filter-name>struts2</filter-name>3 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>4 </filter>5 <filter-mapping>6 <filter-name>struts2</filter-name>7 <url-pattern>/*</url-pattern>8 </filter-mapping>
在web.xml配置文件中我们都有配置StrutsPrepareAndExecuteFilter这就意味着StrutsPrepareAndExecuteFilter拦截器在tomcat启动之时就比加载,并且拦截所有的action请求。
StrutsPrepareAndExecuteFilter中的doFilter如下:
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 = 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); } }
在StrutsPrepareAndExecuteFilter的dofilter方法中我们可以看到一个一行 this.prepare.createActionContext(request, response); 这样的代码其中传入了 HttpServletRequest和HttpServletResponse。我们继续往createActionContext 方法看。
PrepareOperations类中createActionContext方法代码:
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) { Integer counter = Integer.valueOf(1); Integer oldCounter = (Integer)request.getAttribute("__cleanup_recursion_counter"); if(oldCounter != null) { counter = Integer.valueOf(oldCounter.intValue() + 1); } ActionContext oldContext = ActionContext.getContext(); ActionContext ctx; if(oldContext != null) { ctx = new ActionContext(new HashMap(oldContext.getContextMap())); } else {
// 创建值栈 ValueStack stack = ((ValueStackFactory)this.dispatcher.getContainer().getInstance(ValueStackFactory.class)).createValueStack();
// 把后面所获取到的map赋值到值栈的Map中 stack.getContext().putAll(this.dispatcher.createContextMap(request, response, (ActionMapping)null));
// 把值栈的map存储到ActionContext中一份 ctx = new ActionContext(stack.getContext()); } request.setAttribute("__cleanup_recursion_counter", counter); ActionContext.setContext(ctx); return ctx; }
在上面的方法中首先判断是否有ActionContext 如果没有的话就创建一个,如果有的话就创建一个值栈并且把后面所获取到的map赋值到值栈的Map中。最后又把值栈的map存储到ActionContext中一份
进一步分析creatContextMap方法:
public Map<String, Object> createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) {
// 封装内置request对象 RequestMap requestMap = new RequestMap(request);
// 封装内置的request对象的参数 HashMap params = new HashMap(request.getParameterMap()); SessionMap session = new SessionMap(request); ApplicationMap application = new ApplicationMap(this.servletContext); HashMap extraContext = this.createContextMap(requestMap, params, session, application, request, response); if(mapping != null) { extraContext.put("struts.actionMapping", mapping); } return extraContext; }
在这个方法中封装了传入的各个内置对象并把各个内置对象和map传给了createContextMap方法
public HashMap<String, Object> createContextMap(Map requestMap, Map parameterMap, Map sessionMap, Map applicationMap, HttpServletRequest request, HttpServletResponse response) { // 存储了所有的map和内置对象,最后次map被返回
HashMap extraContext = new HashMap(); extraContext.put("com.opensymphony.xwork2.ActionContext.parameters", new HashMap(parameterMap)); extraContext.put("com.opensymphony.xwork2.ActionContext.session", sessionMap); extraContext.put("com.opensymphony.xwork2.ActionContext.application", applicationMap); Locale locale; if(this.defaultLocale != null) { locale = LocalizedTextUtil.localeFromString(this.defaultLocale, request.getLocale()); } else { locale = request.getLocale(); } extraContext.put("com.opensymphony.xwork2.ActionContext.locale", locale); extraContext.put("com.opensymphony.xwork2.dispatcher.HttpServletRequest", request); extraContext.put("com.opensymphony.xwork2.dispatcher.HttpServletResponse", response); extraContext.put("com.opensymphony.xwork2.dispatcher.ServletContext", this.servletContext); extraContext.put("request", requestMap); extraContext.put("session", sessionMap); extraContext.put("application", applicationMap); extraContext.put("parameters", parameterMap); AttributeMap attrMap = new AttributeMap(extraContext); extraContext.put("attr", attrMap);
// 返回给ActionConetext和值栈。 return extraContext; }
综上:
在项目启动时的时候Struts的过滤器把相应的内置对象和内置对象的相应的map存入ActionContest和值栈中。
如果实现了***Aware接口,就会从ActionContest中获取map并传入,该功能主要有servletConfig拦截器实现:
public String intercept(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); ActionContext context = invocation.getInvocationContext(); HttpServletRequest servletContext; if(action instanceof ServletRequestAware) { servletContext = (HttpServletRequest)context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest"); ((ServletRequestAware)action).setServletRequest(servletContext); } if(action instanceof ServletResponseAware) { HttpServletResponse servletContext1 = (HttpServletResponse)context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"); ((ServletResponseAware)action).setServletResponse(servletContext1); } if(action instanceof ParameterAware) { ((ParameterAware)action).setParameters(context.getParameters()); } if(action instanceof ApplicationAware) { ((ApplicationAware)action).setApplication(context.getApplication()); } if(action instanceof SessionAware) { ((SessionAware)action).setSession(context.getSession()); } if(action instanceof RequestAware) { ((RequestAware)action).setRequest((Map)context.get("request")); } if(action instanceof PrincipalAware) { servletContext = (HttpServletRequest)context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest"); if(servletContext != null) { ((PrincipalAware)action).setPrincipalProxy(new ServletPrincipalProxy(servletContext)); } } if(action instanceof ServletContextAware) { ServletContext servletContext2 = (ServletContext)context.get("com.opensymphony.xwork2.dispatcher.ServletContext"); ((ServletContextAware)action).setServletContext(servletContext2); } return invocation.invoke(); }
通过判断是否实现相应的接口来获取相应的map和内置对象。
Struts Actioncontext 和ServletConfigInterceptor的原理分析