Struts 運行流程以及值棧小記

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/qq_34560242/article/details/80963360

一、Struts運行流程圖先了解一下:
這裏寫圖片描述

引用網上的總結:
一個請求在Struts2框架中的處理大概分爲以下幾個步驟:
1、客戶端初始化一個指向Servlet容器(例如Tomcat)的請求
2、這個請求經過一系列的過濾器(Filter)(這些過濾器中有一個叫做ActionContextCleanUp的可選過濾器,這個過濾器對於Struts2和其他框架的集成很有幫助,例如:SiteMesh Plugin)
3、接着FilterDispatcher(現已過時)被調用,FilterDispatcher詢問ActionMapper來決定這個請是否需要調用某個Action
4、如果ActionMapper決定需要調用某個Action,FilterDispatcher把請求的處理交給ActionProxy
5、ActionProxy通過Configuration Manager詢問框架的配置文件,找到需要調用的Action類
6、ActionProxy創建一個ActionInvocation的實例。
7、ActionInvocation實例使用命名模式來調用,在調用Action的過程前後,涉及到相關攔截器(Intercepter)的調用。
8、一旦Action執行完畢,ActionInvocation負責根據struts.xml中的配置找到對應的返回結果。返回結果通常是(但不總是,也可 能是另外的一個Action鏈)一個需要被表示的JSP或者FreeMarker的模版。在表示的過程中可以使用Struts2 框架中繼承的標籤。在這個過程中需要涉及到ActionMapper。

二、值棧:簡單的說,就是存放action的堆棧,當我們提交一個請求道服務器端 action時,就有個堆棧,如果action在服務器端進行跳轉,所有action共用一個堆棧,當需要保存在action中的數據時,首先從棧頂開始 搜索,若找到相同的屬性名(與要獲得的數據的屬性名相同)時,即將值取出。但這種情況可能出現找到的值不是我們想要的值,那麼解決此問題需要用TOP語法 和N語法來進行解決。

三、值棧是怎樣創建的以及值棧和ActionContext的關係

  • 1、Struts的值棧的核心類是ValueStack接口,主要是其實現類OgnlValueStack
// 忽略其它屬性,只看下面兩個
protected CompoundRoot root;
// 這個就是常說的contextMap
protected transient Map<String, Object> context;
// ... ...
  • 2、Struts的核核心過濾器StrutsPrepareAndExecuteFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;

    try {
        String uri = RequestUtils.getUri(request);
        if (this.excludedPatterns != null && this.prepare.isUrlExcluded(request, this.excludedPatterns)) {
            LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
            chain.doFilter(request, response);
        } else {
            LOG.trace("Checking if {} is a static resource", uri);
            boolean handled = this.execute.executeStaticResourceRequest(request, response);
            if (!handled) {
                LOG.trace("Assuming uri {} as a normal action", uri);
                this.prepare.setEncodingAndLocale(request, response);
                // 1、值棧是伴隨着ActionContext的創建而創建的
                this.prepare.createActionContext(request, response);
                this.prepare.assignDispatcherToThread();
                request = this.prepare.wrapRequest(request);
                ActionMapping mapping = this.prepare.findActionMapping(request, response, true);
                if (mapping == null) {
                    LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
                    chain.doFilter(request, response);
                } else {
                    LOG.trace("Found mapping {} for {}", mapping, uri);
                    this.execute.executeAction(request, response, mapping);
                }
            }
        }
    } finally {
        this.prepare.cleanupRequest(request);
    }

}
  • 3、ValueStack伴隨ActionContext創建
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
    Integer counter = 1;
    Integer oldCounter = (Integer)request.getAttribute("__cleanup_recursion_counter");
    if (oldCounter != null) {
        counter = oldCounter + 1;
    }

    ActionContext oldContext = ActionContext.getContext();
    ActionContext ctx;
    if (oldContext != null) {
        ctx = new ActionContext(new HashMap(oldContext.getContextMap()));
    } else {
     // 2、創建值棧
        ValueStack stack = ((ValueStackFactory)this.dispatcher.getContainer().getInstance(ValueStackFactory.class)).createValueStack();
        // 3、創建contextMap,同時也將其壓入值棧的context中,畢竟這樣才能稱其爲contextMap
        stack.getContext().putAll(this.dispatcher.createContextMap(request, response, (ActionMapping)null));
        // 4、將值棧的context作爲構造參數傳給ActionContext
        ctx = new ActionContext(stack.getContext());
    }

    request.setAttribute("__cleanup_recursion_counter", counter);
    // 5、將ActionContext與ThreadLocal綁定
    ActionContext.setContext(ctx);
    return ctx;
}

/** 第二步這裏展開:*/
public ValueStack createValueStack() {
    // 創建一個新的值棧
     ValueStack stack = new OgnlValueStack(this.xworkConverter, this.compoundRootAccessor, this.textProvider, this.allowStaticMethodAccess);
     this.container.inject(stack);
     stack.getContext().put("com.opensymphony.xwork2.ActionContext.container", this.container);
     return stack;
 }

// 如何創建新的值值棧
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticAccess) {
    // root賦值
    this.setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticAccess);
    this.push(prov);
}

protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticMethodAccess) {
    // 初始化root
    this.root = compoundRoot;
    this.securityMemberAccess = new SecurityMemberAccess(allowStaticMethodAccess);
    // 創建默認的Context
    this.context = Ognl.createDefaultContext(this.root, accessor, new OgnlTypeConverterWrapper(xworkConverter), this.securityMemberAccess);
    // 這裏可以看到contexMap維護了一個值棧本身的引用
    this.context.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", this);
    Ognl.setClassResolver(this.context, accessor);
    ((OgnlContext)this.context).setTraceEvaluations(false);
    ((OgnlContext)this.context).setKeepLastEvaluation(false);
}

// 
public static Map addDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess, Map context) {
// 值棧的contextMap的實際數據結構其實就是OgnlContext
    OgnlContext result;

    // ... ...

    // 這裏可以看到contexMap也維護了一個root引用
    result.setRoot(root);
    return result;
}
  • 4、contextMap的創建
public Map<String, Object> createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) {
    Map requestMap = new RequestMap(request);
    HttpParameters params = HttpParameters.create(request.getParameterMap()).build();
    Map session = new SessionMap(request);
    Map application = new ApplicationMap(this.servletContext);
    // 創建contextMap
    Map<String, Object> extraContext = this.createContextMap(requestMap, params, session, application, request, response);
    if (mapping != null) {
        extraContext.put("struts.actionMapping", mapping);
    }

    return extraContext;
}

// 在contextMap中添加各種域對象的引用
public HashMap<String, Object> createContextMap(Map requestMap, HttpParameters parameters, Map sessionMap, Map applicationMap, HttpServletRequest request, HttpServletResponse response) {
    HashMap<String, Object> extraContext = new HashMap();
    extraContext.put("com.opensymphony.xwork2.ActionContext.parameters", parameters);
    extraContext.put("com.opensymphony.xwork2.ActionContext.session", sessionMap);
    extraContext.put("com.opensymphony.xwork2.ActionContext.application", applicationMap);
    extraContext.put("com.opensymphony.xwork2.ActionContext.locale", this.getLocale(request));
    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", parameters);
    AttributeMap attrMap = new AttributeMap(extraContext);
    extraContext.put("attr", attrMap);
    return extraContext;
}
  • 5、將值棧的context作爲構造參數傳給ActionContext
private Map<String, Object> context;

public ActionContext(Map<String, Object> context) {
    // 也就是說將值棧(OgnlValueStack)的contextMap屬性中的所有東西都給了ActionContext
    this.context = context;
}

// 這裏說明從ActionContext中獲取東西基本也都是從contexMap中獲取
public String getName() {
    return (String)this.get("com.opensymphony.xwork2.ActionContext.name");
}

public Object get(String key) {
    return this.context.get(key);
}
  • 6、將ActionContext與ThreadLocal綁定
// 新創建的ActionContext綁定到了ThreadLocal上。
// ThreadLocal的set方法是將ThreadLocal對象和數據對象作爲鍵值對存入線程對象內部的一個Map類型的數據結構裏
// 因此,由於ActionContext被綁定在ThreadLocal對象上,所以ActionContext是線程安全的。
static ThreadLocal<ActionContext> actionContext = new ThreadLocal();

public static void setContext(ActionContext context) {
    actionContext.set(context);
}

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

四、爲什麼請求過來的時候Action類中的屬性可能直接獲取到值呢?

  • 1、當Action接收到請求之後,會先創建一個Action實例,但是這個時候並不會馬上調用Action中的方法。
  • 2、而是會將Action類中屬性先放到值棧(ValueStack)中,這個時候所有的屬性沒有值或者只有對應類型的默認值。
  • 3、這個時候呢,Struts會先調用攔截器鏈中的各種攔截器(這裏可對ValueStack中值做操作,最後在Action中得到操作後的值),調用完攔截器之後;會將值棧(ValueStack)中的屬性賦值給Action類中相應的屬性。
  • 4、然後纔會調用Action中的相應方法,這個時候我們就能直接獲取到這些值了。

五、總結

  • 1、值棧和ActionContext是一起創建的,一次請求創建一次。
  • 2、值棧(OgnlValueStack)包含兩塊:CompoundRoot(ArrayList) root和 Map context(常說的contextMap)。
  • 3、contextMap 中 維護了值棧本身和root兩個引用。
  • 4、因爲ActionContext 中維護了contextMap引用,contextMap中以維護了值棧本身的引用,所以ActionContext是間接引用了值棧(直接說ActionContext中維護了值棧的引用並不合適)。
  • 5、Action類中的屬性可能直接獲取到值是因爲在請求到達Action之前,Struts已經將值提前存放到值棧中了,並在調用方法之前將這些值賦值給對應的屬性。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章