關於struts2中ActionContext的實現原理

   北京,霧霾天氣阻止了今天的馬拉松之行,蝸居一天。爲一個問題「struts2如何保證ActionContext每次取的都是本次請求所對應的實例?」,給一個網友解釋了半天。java

   首先,咱們知道,struts2struts1的一個重要區別就是它進行了Action類和Servlet的解耦。而又提供了獲取Servlet API的其它通道,就是ActionContext(別跟我說還有個ServletActionContext,其實ServletActionContext只是ActionContext的一個子類而已)。源碼爲證:編程

public class ServletActionContext extends ActionContext implements StrutsStatics

   其次,他也知道,ActionContextAction執行時的上下文,能夠看做是一個容器,而且這個容器只是一個Map而已,在容器中存放的是Action在執行時須要用到的VALUE_STACKACTION_NAMESESSIONAPPLICATIONACTION_INVOCATION等等對象,還能夠存放自定義的一些對象。我想用過struts2的朋友們,大多也都知道這些吧。session

   第三,他奇怪的是,在一個請求的處理過程攔截器、action類和result中任什麼時候候獲取的ActionContext都是跟當前請求綁定那一個。爲何!?多線程

 

我給他的建議是,帶着問題讀源碼,呵呵。那咱們一塊兒來看看吧:app

首先ActionContext類的源碼:ide

public class ActionContext implements Serializable{
  static ThreadLocal actionContext = new ThreadLocal();
  public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name";
  public static final String VALUE_STACK = "com.opensymphony.xwork2.util.ValueStack.ValueStack";
  public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session";
  public static final String APPLICATION = "com.opensymphony.xwork2.ActionContext.application";
  public static final String PARAMETERS = "com.opensymphony.xwork2.ActionContext.parameters";
  public static final String LOCALE = "com.opensymphony.xwork2.ActionContext.locale";
  public static final String TYPE_CONVERTER = "com.opensymphony.xwork2.ActionContext.typeConverter";
  public static final String ACTION_INVOCATION = "com.opensymphony.xwork2.ActionContext.actionInvocation";
  public static final String CONVERSION_ERRORS = "com.opensymphony.xwork2.ActionContext.conversionErrors";
  public static final String CONTAINER = "com.opensymphony.xwork2.ActionContext.container";
  Map<String, Object> context;
  public ActionContext(Map<String, Object> context)
  {
    this.context = context;
  }
  //... ...
  public static void setContext(ActionContext context)
  {
    actionContext.set(context);
  }
  public static ActionContext getContext()
  {
    return (ActionContext)actionContext.get();
  }
  public void setContextMap(Map<String, Object> contextMap)
  {
    getContext().context = contextMap;
  }
  public Map<String, Object> getContextMap()
  {
    return this.context;
  }
  //... ...
  public void setSession(Map<String, Object> session)
  {
    put("com.opensymphony.xwork2.ActionContext.session", session);
  }
  public Map<String, Object> getSession()
  {
    return (Map)get("com.opensymphony.xwork2.ActionContext.session");
  }
  //... ...
  public Object get(String key)
  {
    return this.context.get(key);
  }
  public void put(String key, Object value)
  {
    this.context.put(key, value);
  }
}

源碼清晰的說明了咱們編程中再熟悉不過的一行代碼:ActionContext ctx = ActionContext.getContext();,原來咱們所取得的ctx來自於ThreadLocal啊!熟悉ThreadLocal的朋友都知道它是與當前線程綁定的,並且是咱們Java中處理多線程問題的一種重要方式。咱們再看,類中有個Map類型的變量context,其實,它纔是前面咱們提到的真正意義上的「容器」,用來存放Action在執行時所須要的那些數據。this

    到這裏,他最初的那個問題已經很瞭然了。可是,他緊接着又一個疑惑提出來了:「那既然每一個請求處理線程都有本身的ActionContext,那裏面的那些數據是何時放進去的呢」?spa

   此次我給他的建議是,動腦筋,用源碼驗證。既然ActionContext存放有HttpServletRequest及其中的參數,既然ActionContext貫穿於整個請求處理過程,那就從struts2請求處理的入口(過濾器StrutsPrepareAndExecuteFilter)找,源碼:線程

public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter
{
  // ... ...
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
    throws IOException, ServletException
  {
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;
    try
    {
      this.prepare.setEncodingAndLocale(request, response);
      this.prepare.createActionContext(request, response);//就是在這裏進行建立並初始化ActionContext實例
      this.prepare.assignDispatcherToThread();
      if ((this.excludedPatterns != null) && (this.prepare.isUrlExcluded(request, this.excludedPatterns))) {
        chain.doFilter(request, response);
      } else {
        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);
    }
  }
   //... ...
}

再找到prepare對應的類PrepareOperations,查看方法createActionContext(),就一目瞭然了。對象

   對於ServletActionContext做爲ActionContext一個直接子類,原理也是相似的,感興趣的朋友能夠看一下。

   幫助別人,同時也是幫助本身。把這個處理的過程記錄下來,但願對須要的朋友有所幫助。

相關文章
相關標籤/搜索