关于struts2中ActionContext的实现原理

本文详细解析了Struts2框架中ActionContext的使用及其与ThreadLocal的关系,解释了ActionContext如何在请求处理过程中保持数据一致性,并通过源码分析了数据注入的时机。此外,文章还探讨了ServletActionContext作为ActionContext子类的作用。
摘要由CSDN通过智能技术生成

首先 ,我们知道, struts2 和 struts1 的一个重要区别就是它进行了 Action 类和Servlet 的解耦。而又提供了获取 Servlet API 的其它通道,就是 ActionContext (别跟我说还有个 ServletActionContext ,其实 ServletActionContext 只是 ActionContext 的一个子类而已)。源码为证:

public class ServletActionContext extends ActionContext implements StrutsStatics

其次 ,他也知道, ActionContext 是 Action 执行时的上下文,可以看作是一个容器,并且这个容器只是一个 Map 而已,在容器中存放的是 Action 在执行时需要用到的VALUE_STACK 、 ACTION_NAME 、 SESSION 、 APPLICATION 、ACTION_INVOCATION 等等对象,还可以存放自定义的一些对象。我想用过 struts2的朋友们,大多也都知道这些吧。

第三 ,他奇怪的是,在一个请求的处理过程拦截器、 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<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 在执行时所需要的那些数据。

到这里 ,他最初的那个问题已经很了然了。但是,他紧接着又一个疑惑提出来了:“那既然每个请求处理线程都有自己的 ActionContext ,那里面的那些数据是什么时候放进去的呢”?

这次 我给他的建议是,动脑筋,用源码验证。既然 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 一个直接子类,原理也是类似的,感兴趣的朋友可以看一下。

帮助别人,同时也是帮助自己。 把这个处理的过程记录下来,希望对需要的朋友有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值