struts 2におけるActionContextの実現原理について


北京、スモッグの天気は今日のマラソンの旅を阻止して、1日蜗居しました.「struts 2は、ActionContextが毎回このリクエストに対応するインスタンスを取得することをどのように保証しますか?」という質問に対して、あるネットユーザーに半日説明した.
まず,struts 2とstruts 1の重要な違いは,Actionクラスとサーブレットのデカップリングを行ったことであることを知った.また、サーブレットAPIを取得する他のチャネルが提供されています.ActionContextです(サーブレットActionContextもあるとは言わないでください.実はサーブレットActionContextはActionContextのサブクラスにすぎません).ソースコード:
public class ServletActionContext extends ActionContext implements StrutsStatics

次に、ActionContextはAction実行時のコンテキストであり、コンテナと見なすことができ、このコンテナはMapにすぎず、実行時にActionが使用するVALUE_が格納されていることも知っている.STACK、ACTION_NAME、SESSION、APPLICATION、ACTION_INVOCATIONなどのオブジェクトは、カスタムオブジェクトを保存することもできます.struts 2を使ったことがある方は、ほとんど知っていると思いますよね.
第三に、彼は不思議なことに、1つのリクエストの処理プロセスブロック、actionクラス、resultでいつ取得したActionContextも現在のリクエストにバインドされている.どうして!?
 
私が彼に提案したのは、問題を持ってソースコードを読むことです.ほほほ.では、一緒に見てみましょう.
まずActionContextクラスのソースコードです.
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 context;
  public ActionContext(Map 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 contextMap)
  {
    getContext().context = contextMap;
  }
  public Map getContextMap()
  {
    return this.context;
  }
  //... ...
  public void setSession(Map session)
  {
    put("com.opensymphony.xwork2.ActionContext.session", session);
  }
  public Map 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の実行に必要なデータを格納するために、前述した本当の意味での「コンテナ」です.
ここまで来て、彼の最初の問題はもうはっきりしている.しかし、「リクエスト処理スレッドごとに独自のActionContextがある以上、その中のデータはいつ入ったのか」という疑問が提起された.
今回私が彼に提案したのは、頭を働かせて、ソースコードで検証することです.ActionContextにHttpServiceletRequestとそのパラメータが格納されている以上、ActionContextがリクエスト処理プロセス全体にわたっている以上、struts 2リクエスト処理のエントリ(フィルタ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()を表示すると、一目瞭然です.
サーブレットActionContextはActionContextの直接サブクラスとして原理も似ているので、興味のある方はご覧ください.
人を助けると同時に、自分を助ける.この処理の過程を記録して、必要な友达に役に立つことを望んでいます.