章節(jié)簡(jiǎn)言 |
上一章筆者寫(xiě)關(guān)于Dispatcher類如何處理接受來(lái)的request請(qǐng)求。當(dāng)然讀者們也知道他并非正真的執(zhí)行action操作。他只是在執(zhí)行action操作之前的準(zhǔn)備工作。那么誰(shuí)才是正真的執(zhí)行action呢?本章筆者就帶大家來(lái)看看StrutsExecuteFilter類的工作。在理解StrutsExecuteFilter類的工作之前,筆者還是希望大家回顧一下前一章講到的request請(qǐng)求工作。為什么這樣子講呢?可以說(shuō)StrutsExecuteFilter類的工作是建立在StrutsPrepareFilter類基礎(chǔ)上運(yùn)行的。先相信這一點(diǎn)筆者不需要聲明了。筆者為了更好的理解小小的做一個(gè)張圖片。如下
從上面的圖片我們就是很清楚StrutsPrepareFilter類做了哪些工作。而圖上的上五點(diǎn)對(duì)于后面的StrutsExecuteFilter類來(lái)講是非常重要的。雖然我在前面幾章也提過(guò)StrutsExecuteFilter類的知識(shí)?!?a class="postTitle2" style="margin: 0px; padding: 0px; outline: none; color: black;">Struts2 源碼分析——過(guò)濾器(Filter)》章節(jié)里面也講過(guò)。只是很簡(jiǎn)單的略講一下。并沒(méi)有對(duì)他進(jìn)特別的講。主要是筆者認(rèn)為不了解StrutsPrepareFilter類的工作的情況下,去了解StrutsExecuteFilter類的話。是一件比較吃力的事情。好了。筆者就不多說(shuō)了。讓我們進(jìn)入本章的內(nèi)容吧。
調(diào)結(jié)者的執(zhí)行action |
StrutsExecuteFilter類的工作就是執(zhí)行對(duì)應(yīng)的action請(qǐng)求。StrutsExecuteFilter類的工作還需要有一個(gè)叫ExecuteOperations類的幫助。如果看過(guò)源碼的朋友都知道,StrutsExecuteFilter類的代碼里用了ExecuteOperations類的倆個(gè)方法。一個(gè)是:executeStaticResourceRequest方法。一個(gè)是:executeAction方法。光從字名面上我就知道他們的功能。executeStaticResourceRequest是執(zhí)行靜態(tài)資源請(qǐng)求。如JS文件,css文件等。而executeAction就是執(zhí)行action請(qǐng)求。即是筆者想要講的重點(diǎn)。好了。還是讓我們先看一下StrutsExecuteFilter類代碼吧。如下部分代碼
StrutsExecuteFilter類:
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 if (excludeUrl(request)) {//用于判斷是否在排除的action之內(nèi)。如果是就跳過(guò)。 7 chain.doFilter(request, response); 8 return; 9 }10 11 if (execute == null) {12 lazyInit();//初始化相關(guān)的信息類。13 }14 15 ActionMapping mapping = prepare.findActionMapping(request, response);//找到ActionMapping實(shí)例16 17 18 Integer recursionCounter = (Integer) request.getAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER);19 20 if (mapping == null || recursionCounter > 1) {21 boolean handled = execute.executeStaticResourceRequest(request, response);//執(zhí)行請(qǐng)求css,js文件。并返回是否成功。22 if (!handled) {23 chain.doFilter(request, response);24 }25 } else {26 execute.executeAction(request, response, mapping);//執(zhí)行action請(qǐng)求,重要部分27 }28 }
根據(jù)上面的紅色的代碼,讓筆者講一下總共做了幾件事件。
1.判斷當(dāng)前的request請(qǐng)求是不是被排在外。如果就跳過(guò)去。(筆者不想過(guò)講,太簡(jiǎn)單了)
2.判斷是否存在ExecuteOperations類的實(shí)例。如果沒(méi)有就初始化。相關(guān)的代碼如下。
StrutsExecuteFilter類:
1 /** 2 * 加載并初始化 3 */ 4 protected synchronized void lazyInit() { 5 if (execute == null) { 6 InitOperations init = new InitOperations();//用于初始化的功能類 7 Dispatcher dispatcher = init.findDispatcherOnThread();//StrutsPrepareFilter類的時(shí)候,就把Dispatcher實(shí)例存放在本地線程里面。這是只是把他拿出來(lái)。 8 init.initStaticContentLoader(new FilterHostConfig(filterConfig), dispatcher);//初始化用于加載css,js文件的加載類。 9 10 prepare = new PrepareOperations(dispatcher);11 execute = new ExecuteOperations(dispatcher);12 }13 14 }
看了代碼我們就知道StrutsExecuteFilter類的lazyInit方法做了什么。
1).找到對(duì)應(yīng)的Dispatcher實(shí)例。那么Dispatcher實(shí)例在哪里初始化呢?這就是StrutsPrepareFilter類的里面。(如果不理解的讀者,請(qǐng)轉(zhuǎn)至Struts2 源碼分析——調(diào)結(jié)者(Dispatcher)之a(chǎn)ction請(qǐng)求的章節(jié))
2).初始化StaticContentLoader類。即是用于加載JS,CSS文件等類似的加載類。
3).初始化相關(guān)對(duì)應(yīng)的PrepareOperations類和ExecuteOperations類。為了下面執(zhí)行action請(qǐng)求準(zhǔn)備。其中ExecuteOperations類很重要。用于執(zhí)行action和加載JS,CSS文件類似的調(diào)動(dòng)者。
3.找到對(duì)應(yīng)的action映射(ActionMapping類)。可以說(shuō)沒(méi)有action映射就沒(méi)有辦法執(zhí)行相關(guān)的action操作。讓我們看一下findActionMapping方法的代碼吧。
PrepareOperations類:
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) { ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY); if (mapping == null || forceLookup) { try { mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager()); if (mapping != null) { request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping); } } catch (Exception ex) { dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); } } return mapping; }
先從request請(qǐng)求中找到以STRUTS_ACTION_MAPPING_KEY常量為Key的ActionMapping值。如果不存在,則通過(guò)Container容器中找到的ActionMapper實(shí)例,并通過(guò)ActionMapper實(shí)例找到對(duì)應(yīng)的Action映射,并存于request請(qǐng)求。其Key值為STRUTS_ACTION_MAPPING_KEY常量。相信讀者又看Dispatcher類的實(shí)例了。又跟他有關(guān)系。關(guān)于這一步其實(shí)在StrutsPrepareFilter類工作的時(shí)候就已經(jīng)做過(guò)一次了。(在這里用到ActionMapper類。關(guān)于他的作用讀者目前只要知道所有的struts.xml上的配置action信息都在里面。筆者后面說(shuō)找一個(gè)章節(jié)講他)
4.如果沒(méi)有找到對(duì)應(yīng)的action映射(ActionMapping類)或action跳越的數(shù)量>1就是執(zhí)行加載JS,CSS文件的加載類。否則就是執(zhí)行action。實(shí)話實(shí)說(shuō)筆者真不知道recursionCounter > 1是什么個(gè)意思。我只能把他理解為跳轉(zhuǎn)的action數(shù)。筆者也做了相關(guān)通的實(shí)驗(yàn)就是希望看出一些事端??上×?。
先看一下executeStaticResourceRequest方法吧。對(duì)于executeStaticResourceRequest方法。筆者在上面就講到了。他是用于加載相關(guān)的靜態(tài)資源。如CSS文件,JS文件。這些文件是在JAR里面的。我們有時(shí)候struts2相關(guān)的UI的TAG的時(shí)候,就要加載對(duì)應(yīng)的CSS文件,和JS文件吧。這個(gè)時(shí)候他就啟作用了。讓我們看一下代碼吧。
ExecuteOperations類:
1 public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { 2 // 如果沒(méi)有找到對(duì)應(yīng)的action,我們應(yīng)該看一下是不是請(qǐng)求靜態(tài)資源 3 String resourcePath = RequestUtils.getServletPath(request); 4 5 if ("".equals(resourcePath) && null != request.getPathInfo()) { 6 resourcePath = request.getPathInfo(); 7 } 8 9 StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class);10 if (staticResourceLoader.canHandle(resourcePath)) {11 staticResourceLoader.findStaticResource(resourcePath, request, response);12 return true;13 14 } else {15 // 如果不是的話,就表示他是一個(gè)普通的請(qǐng)求16 return false;17 }18 }
因?yàn)檫@部分不是筆者這系列要講的重點(diǎn)。如果有興趣的讀者可以自行繼續(xù)研發(fā)下去。我們可以看又是跟Dispatcher類的實(shí)例有關(guān)系。相信讀者這個(gè)時(shí)候很能明白筆者為什么說(shuō)Dispatcher類很重要。很能做很多事情。
關(guān)于執(zhí)行action的部分就在executeAction方法里面。讓我們看一下代碼吧。
ExecuteOperations類:
1 public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {2 dispatcher.serviceAction(request, response, mapping);3 }
好吧。我有一種打人的沖動(dòng)。Dispatcher類的實(shí)例又出現(xiàn)。執(zhí)行request請(qǐng)求的action也是Dispatcher類的實(shí)例來(lái)完成的。既然如此讓我們看一下代碼吧。如下
1 public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) 2 throws ServletException { 3 4 Map<String, Object> extraContext = createContextMap(request, response, mapping); 5 6 //如果之前就有了值棧,就是新建一個(gè)新的值棧,放入extraContext 7 ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); 8 boolean nullStack = stack == null; 9 if (nullStack) {10 ActionContext ctx = ActionContext.getContext();11 if (ctx != null) {12 stack = ctx.getValueStack();13 }14 }15 if (stack != null) {16 extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));17 }18 19 String timerKey = "Handling request from Dispatcher";20 try {21 UtilTimerStack.push(timerKey);22 String namespace = mapping.getNamespace();//獲得request請(qǐng)求里面的命名空間,即是struts.xml是的package節(jié)點(diǎn)元素 23 String name = mapping.getName();//獲得request請(qǐng)求里面的action名 24 String method = mapping.getMethod();//要執(zhí)行action的方法25 26 ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name,27 method, extraContext, true, false);//獲得action的代理28 29 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());30 31 // 如果action映射是直接就跳轉(zhuǎn)到網(wǎng)頁(yè)的話, 32 if (mapping.getResult() != null) { 33 Result result = mapping.getResult(); 34 result.execute(proxy.getInvocation()); 35 } else { 36 proxy.execute();//這里就是執(zhí)行action 37 }38 39 40 41 if (!nullStack) {42 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);43 }44 } catch (ConfigurationException e) {45 logConfigurationException(request, e);46 sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);47 } catch (Exception e) {48 if (handleException || devMode) {49 sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);50 } else {51 throw new ServletException(e);52 }53 } finally {54 UtilTimerStack.pop(timerKey);55 }56 }
從上面的代碼就能看出在執(zhí)行action的內(nèi)部還需要用到一個(gè)叫ActionProxy類。關(guān)于這部分知識(shí),筆者其實(shí)這里不想講的很細(xì)。主要這部分的知識(shí)太多了。但這里筆者還是希望為后面的章節(jié)做好準(zhǔn)備。ActionProxy類可以理解他是一個(gè)代理。他的主要目地就是根據(jù)action映射得到的信息,尋找對(duì)應(yīng)action類實(shí)例,然后執(zhí)行對(duì)應(yīng)的方法。其中包括加載對(duì)應(yīng)的攔截器,初始化相應(yīng)的結(jié)果。而這段代碼中,在ActionProxy類的execute()方法的時(shí)候,還作了相應(yīng)的判斷。即是是否直接回返結(jié)果。其次還有在講到一個(gè)關(guān)于值棧的知識(shí)。這里在獲得ActionProxy類實(shí)例的時(shí)候,需要得到對(duì)應(yīng)值棧的信息。但是不管如何,最后一定會(huì)把request請(qǐng)求的值棧重新更新一下。ValueStack(值棧)的作用相信大家都懂。我就不做過(guò)多的講解了。
本章總結(jié) |
可以說(shuō)相關(guān)Dispatcher類的知識(shí)點(diǎn),到本章節(jié)算是結(jié)束了。筆者把Dispatcher類的功能分為三點(diǎn):一是加載struts2運(yùn)行的必要條件信息;二是初始化action請(qǐng)求需要的信息;三是執(zhí)行request請(qǐng)求對(duì)應(yīng)的action。而關(guān)于核心機(jī)制圖片的橙色部分的工作大部分筆者都有體現(xiàn)出來(lái)。而后面的章節(jié)都是為了這三個(gè)功能點(diǎn)進(jìn)行的。所以希望讀者能理解這三個(gè)功能。