調(diào)結(jié)者的action請求

StrutsPrepareFilter類在處理request請求的時候,需要用到一個叫PrepareOperations類的幫忙。PrepareOperations類可以說是StrutsPrepareFilter類和Dispatcher類的中間人。PrepareOperations類大部分的工作都是通過Dispatcher類完成的。先讓我們看一段代碼。如下

StrutsPrepareFilter類:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

  public void init(FilterConfig filterConfig) throws ServletException {
        InitOperations init = new InitOperations();//用于初始化相關(guān)的功能操作。你可以理解為工具類一樣子。
        Dispatcher dispatcher = null;//這個類相當(dāng)?shù)闹匾?。他的作用連接著StrutsExecuteFilter。這里可以命名為調(diào)結(jié)者。
        try {
            FilterHostConfig config = new FilterHostConfig(filterConfig);//這里可以理解為把filterConfig在進(jìn)行封裝FilterHostConfig更為主便操作和理解。
            init.initLogging(config);//獲取名為loggerFactory的參數(shù),并實例化這個類。一般為去用戶自定義日志。
            dispatcher = init.initDispatcher(config);//初化調(diào)結(jié)者。這里是重要。
            prepare = new PrepareOperations(dispatcher);            this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);//加載排除在內(nèi)的action的正則表達(dá)式
            postInit(dispatcher, filterConfig);
        } finally {            if (dispatcher != null) {
                dispatcher.cleanUpAfterInit();
            }
            init.cleanup();
        }
    }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

從上面的紅色代碼我們可以看出來,PrepareOperations類在實例化時候,接受Dispatcher類作為構(gòu)造函數(shù)的參數(shù)。即是在struts2啟動加載準(zhǔn)備工作之后初始化。那么PrepareOperations類到底又做哪些工作呢?讓我們在看一下下面的代碼。如下

StrutsPrepareFilter類:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 1  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 2  3         HttpServletRequest request = (HttpServletRequest) req; 4         HttpServletResponse response = (HttpServletResponse) res; 5  6         try { 7             if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { 8                 request.setAttribute(REQUEST_EXCLUDED_FROM_ACTION_MAPPING, new Object()); 9             } else {10                 prepare.setEncodingAndLocale(request, response);//設(shè)置請求的格式編碼。
11                 prepare.createActionContext(request, response);//action的上下文
12                 prepare.assignDispatcherToThread();//把Dispatcher放入本地線程里面。
13                 request = prepare.wrapRequest(request);
14                 prepare.findActionMapping(request, response);//找到action映射的信息15             }16             chain.doFilter(request, response);17         } finally {18             prepare.cleanupRequest(request);19         }20     }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

上面代碼我們可以看出PrepareOperations類總共做了五件事情。先讓筆者簡單的講解一下:當(dāng)用戶的request請求過來的時候,會判斷一下request請求是不是被排除之外的。如果是,則把REQUEST_EXCLUDED_FROM_ACTION_MAPPING常量作為KEY,object實例作為值存放在request的Attrbute里面。這是為后面的StrutsExecuteFilter類作準(zhǔn)備(下一章筆者會講到)。如果不是,則進(jìn)行request請求處理。如下

1.設(shè)置request請求的本地化和格式編碼。實現(xiàn)上還是Dispatcher類在做工作。代碼如下

PrepareOperations類:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

1   /**2      * 設(shè)置本地化和請求格式編碼3      *4      * @param request servlet request5      * @param response servlet response6      */7     public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) {8         dispatcher.prepare(request, response);9     }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

Dispatcher類:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 1 /** 2      * 設(shè)置請求的本地化和格式編碼 3      * 4      * @param request 5      *            The request 6      * @param response 7      *            The response 8      */ 9     public void prepare(HttpServletRequest request, HttpServletResponse response) {10         String encoding = null;11         if (defaultEncoding != null) {12             encoding = defaultEncoding;13         }14 15         if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {16             encoding = "UTF-8";17         }18 19         Locale locale = null;20         if (defaultLocale != null) {21             locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());22         }23 24         if (encoding != null) {25             applyEncoding(request, encoding);26         }27 28         if (locale != null) {29             response.setLocale(locale);30         }31 32         if (paramsWorkaroundEnabled) {33             request.getParameter("foo"); // simply read any parameter (existing34                                             // or not) to "prime" the request35         }36     }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 2.創(chuàng)建Action上下文(ActionContext類),并把上下文存放在本地線程(ThreadLocal)。所謂的上下文可以理解為把相同性質(zhì)的業(yè)務(wù)歸為一類。而上下文就是這一類和外部相交處。所有的數(shù)據(jù)操作都可以通過他還完成。當(dāng)然上下文的定義在不同的地方有不同的意思。請讀者自行找閱資料。先讓我們看一下代碼。如下

PrepareOperations類:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 1  /** 2      * 創(chuàng)建Action的上下文 ,并存在到本地線程 3      * 4      * @param request servlet request 5      * @param response servlet response 6      * 7      * @return the action context 8      */ 9     public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {10         ActionContext ctx;11         Integer counter = 1;12         Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);13         if (oldCounter != null) {14             counter = oldCounter + 1;15         }16         17         ActionContext oldContext = ActionContext.getContext();18         if (oldContext != null) {19             // 有舊的action上下文,我們可以認(rèn)為有可能是跳轉(zhuǎn)。20             ctx = new ActionContext(new HashMap<>(oldContext.getContextMap()));21         } else {22             ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();//從容器中獲得值棧工廠,并新建值棧。
23             stack.getContext().putAll(dispatcher.createContextMap(request, response, null));//創(chuàng)建上下MAP并合到值棧里面去。
24             ctx = new ActionContext(stack.getContext());//用值棧的上下MAP來新建上下文25         }26         request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);27         ActionContext.setContext(ctx);28         return ctx;29     }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

從上面的紅色代碼我們就能夠看出第一次request請求就會創(chuàng)建一個新的action上下文(ActionContext)。同時也能明白ActionContext里面存放大量的關(guān)于request請求對應(yīng)的數(shù)據(jù)。而這些操作又離不開Dispatcher類的幫忙。最后把上下文(ActionContext類)存放到ActionContext類內(nèi)部的本地線程(ThreadLocal)。代碼ActionContext.setContext(ctx)就是最好的說明。另外,上面有一點讓筆者一直不明白為什么這么做。覺得沒有什么意義。即是獲得request的屬性為CLEANUP_RECURSION_COUNTER的值,然后進(jìn)行計算操作的功能。筆者不得不將他理解為:用于計算request請求跳轉(zhuǎn)action的次數(shù)。就是一個request請求的生命周期通過了幾個action請求。如果不對的話,請讀者自行屏蔽。

3.把Dispatcher實例分配置到他內(nèi)部的本地線程(ThreadLocal)。這一步主要是為了后面的StrutsExecuteFilter類的工作。讓我們看一下代碼吧。如下

PrepareOperations類:

1   /**2      * dispatcher分配到Dispatcher類的本地線程3      */4     public void assignDispatcherToThread() {5         Dispatcher.setInstance(dispatcher);6     }

4.把HttpServletRequest包裝為對應(yīng)的StrutsRequestWrapper或是MultiPartRequestWrapper。主要是為了方便開發(fā)人員操作處理multipart而以。這里比較簡單。筆者不想過的解釋。

5.找到對應(yīng)action映射信息(ActionMapping類)。這部分的工作筆者認(rèn)為是比較重要的。因為他將是StrutsExecuteFilter類工作的核心點。那么ActionMapping類又是什么呢?他是struts.xml配置文件上的action信息。有了他struts2才能知道當(dāng)前請求是哪一個action類。那么相關(guān)的知識筆者會在后面的章節(jié)講到。讓我們看一下代碼。如下

PrepareOperations類:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 1  public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) { 2         ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY); 3         if (mapping == null || forceLookup) { 4             try { 5                 mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager()); 6                 if (mapping != null) { 7                     request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping); 8                 } 9             } catch (Exception ex) {10                 dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);11             }12         }13 14         return mapping;15     }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

當(dāng)筆者看到紅色代碼的時候,不得不說一句???!又離不開Dispatcher類。同時值得注意是的ActionMapper類。所有的struts.xml配置文件的action信息都在這個類上面。即是可以通過ActionMapper類找到對應(yīng)的action映射信息(ActionMapping)。從而找到對應(yīng)的action類(用戶定義的action)。另外代碼request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping)這邊所做的事情。功能意思大家都能看得出來。那為什么這么做。主要還是因為后面的StrutsExecuteFilter類要用到。

從上面的五個事件中。筆者至少知道一點。Dispatcher類的工作真的很重要。而PrepareOperations類大部分只是一個中間人而以。當(dāng)然這是筆者自己的理解。

Dispatcher的結(jié)束處理

筆者本來想把這個知識點做一個章節(jié)來講??墒窍胩倭恕槭裁词墙Y(jié)束工作呢?不管是struts2啟動的準(zhǔn)備工作。還是啟動成功后的action請求工作。struts2都會在工作結(jié)束之進(jìn)行一些處理。先看一下struts2啟動的準(zhǔn)備工作成完之后的處理。如下

StrutsPrepareFilter類的init方法:

1  if (dispatcher != null) {2                 dispatcher.cleanUpAfterInit();3             }4             init.cleanup();

Dispatcher類:

1     public void cleanUpAfterInit() {2         if (LOG.isDebugEnabled()) {3             LOG.debug("Cleaning up resources used to init Dispatcher");4         }5         ContainerHolder.clear();6     }

InitOperations類:

1  public void cleanup() {2         ActionContext.setContext(null);3     }

從上面的代碼中我們可以看一個叫ContainerHolder類。這個類主要是用于存放Container容器。而上面在struts2啟動加載相關(guān)信息的準(zhǔn)備工作結(jié)束之后。把Container容器給冊除了。同時也去掉了上下文(ActionContext類)。

讓我們看一下request請求結(jié)束后做了什么。如下

StrutsPrepareFilter類的doFilter方法:

1 prepare.cleanupRequest(request);

PrepareOperations類:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 1 public void cleanupRequest(HttpServletRequest request) { 2         Integer counterVal = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER); 3         if (counterVal != null) { 4             counterVal -= 1; 5             request.setAttribute(CLEANUP_RECURSION_COUNTER, counterVal); 6             if (counterVal > 0 ) { 7                 LOG.debug("skipping cleanup counter={}", counterVal); 8                 return; 9             }10         }11         // always clean up the thread request, even if an action hasn't been executed12         try {13             dispatcher.cleanUpRequest(request);14         } finally {15             ActionContext.setContext(null);16             Dispatcher.setInstance(null);17             devModeOverride.remove();18         }19     }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

Dispatcher類:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

1     public void cleanUpRequest(HttpServletRequest request) {2         ContainerHolder.clear();3         if (!(request instanceof MultiPartRequestWrapper)) {4             return;5         }6         MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;7         multiWrapper.cleanUp();8     }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

從上面的紅色代碼我們知道他在request請求結(jié)束之后,把Container容器給冊除了。本地線程的Dispatcher類的實例刪除了。上下文(ActionContext類)刪除了。

到了這里筆者就明白了一點: