Struts2 源碼分析——DefaultActionInvocation類的執行action


本章簡言

上一章講到關於攔截器的機制的知識點,讓我們對攔截器有了一定的認識。我們也清楚的知道在執行用戶action類實例之前,struts2會先去執行當前action類對應的攔截器。而關於在哪里執行action類實例,筆者根本就沒有詳細的講到。更多只是幾筆帶過而以。雖然在《Struts2 源碼分析——Action代理類的工作》章節里面也講到過關於DefaultActionInvocation類的一些作用。提過DefaultActionInvocation類會去執行action類實例。但是還是沒有具體的指出重點的代碼。而本章就是來講執行action類實例的代碼落在哪里。即是DefaultActionInvocation類的invokeAction方法。

 DefaultActionInvocation類的執行action

上一章里面有提到過DefaultActionInvocation類的invoke方法里面的invokeActionOnly方法。沒有錯!當所有攔截器前半部分執行結束之后,就會去執行invokeActionOnly方法。這個方法就是執行action類實例的入口。而invokeActionOnly方法實際是去調用本身類的invokeAction方法。看一下代碼就知道了。

DefaultActionInvocation類:

1 public String invokeActionOnly() throws Exception {
2         return invokeAction(getAction(), proxy.getConfig());
3     }

代碼里面getAction方法就是獲得action類實例。即是跟《Struts2 源碼分析——Action代理類的工作》章節里面講到的createAction方法有關。當程序執行到這里的時候,createAction方法已經新建好了action類實例。不清楚讀者請到這章去看一下。讓我們看一下invokeAction方法的源碼吧。

 1 protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
 2         String methodName = proxy.getMethod();//獲得要執行的方法名。
 3 
 4         LOG.debug("Executing action method = {}", methodName);
 5 
 6         String timerKey = "invokeAction: " + proxy.getActionName();
 7         try {
 8             UtilTimerStack.push(timerKey);
 9 
10             Object methodResult;
11             try {
12                 methodResult = ognlUtil.callMethod(methodName + "()", getStack().getContext(), action);//執行action類實例
13             } catch (MethodFailedException e) {
14                 // if reason is missing method,  try checking UnknownHandlers
15                 if (e.getReason() instanceof NoSuchMethodException) {
16                     if (unknownHandlerManager.hasUnknownHandlers()) {
17                         try {
18                             methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
19                         } catch (NoSuchMethodException ignore) {
20                             // throw the original one
21                             throw e;
22                         }
23                     } else {
24                         // throw the original one
25                         throw e;
26                     }
27                     // throw the original exception as UnknownHandlers weren't able to handle invocation as well
28                     if (methodResult == null) {
29                         throw e;
30                     }
31                 } else {
32                     // exception isn't related to missing action method, throw it
33                     throw e;
34                 }
35             }
36             return saveResult(actionConfig, methodResult);
37         } catch (NoSuchPropertyException e) {
38             throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
39         } catch (MethodFailedException e) {
40             // We try to return the source exception.
41             Throwable t = e.getCause();
42 
43             if (actionEventListener != null) {
44                 String result = actionEventListener.handleException(t, getStack());
45                 if (result != null) {
46                     return result;
47                 }
48             }
49             if (t instanceof Exception) {
50                 throw (Exception) t;
51             } else {
52                 throw e;
53             }
54         } finally {
55             UtilTimerStack.pop(timerKey);
56         }
57     }

這個方法的做的事情是很簡單。就是獲得當前action類實例要執行的方法名。在根據OgnlUtil工具類在執行對應action類實例的方法。顯然想要知道更深一點的話就必須深入OgnlUtil工具類的源碼。讓我們看一下吧。

OgnlUtil類:

1 public Object callMethod(final String name, final Map<String, Object> context, final Object root) throws OgnlException {
2         return compileAndExecuteMethod(name, context, new OgnlTask<Object>() {
3             public Object execute(Object tree) throws OgnlException {
4                 return Ognl.getValue(tree, context, root);
5             }
6         });
7     }

OgnlUtil類:

private <T> Object compileAndExecuteMethod(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
        Object tree;
        if (enableExpressionCache) {
            tree = expressions.get(expression);
            if (tree == null) {
                tree = Ognl.parseExpression(expression);
                checkSimpleMethod(tree, context);
            }
        } else {
            tree = Ognl.parseExpression(expression);
            checkSimpleMethod(tree, context);
        }

        final T exec = task.execute(tree);
        // if cache is enabled and it's a valid expression, puts it in
        if(enableExpressionCache) {
            expressions.putIfAbsent(expression, tree);
        }
        return exec;
    }

筆者看到這里的時候就有一點心煩。主要是一看就知道又要去學習一下關於ONGL相關的語法。這一點請讀者自己去補充。Ognl.getValue(tree, context, root)這句代碼就是ONGL語法的體現。其中tree就是ONGL的表達式。root就是根對象。即是用戶action類實例。對於context筆者還真不知道要什么去講解他。筆者把他理解為ONGL的上下文。通常跟ONGL表達式里面的“#”號相關。簡單講就是Ognl.getValue(tree, context, root)就是執行action方法。而接下深入就是ognl.jar的源碼了。已經跳出了struts2源碼的范圍了。

執行完上面的代碼之后,就開始保存相應的結果。saveResult方法就是用於處理結果的。如果結果值是Result類型的話,就把他存在成員變量explicitResult上,並返回NULL。否則就轉為String類型並返回。代碼如下

 1 protected String saveResult(ActionConfig actionConfig, Object methodResult) {
 2         if (methodResult instanceof Result) {
 3             this.explicitResult = (Result) methodResult;
 4 
 5             // Wire the result automatically
 6             container.inject(explicitResult);
 7             return null;
 8         } else {
 9             return (String) methodResult;
10         }
11     }

如果筆者沒有查看源碼的話,就不會知道原來action類實例在執行結束之后不只是一個String類型。還有一個叫Result類。如果有硬的講的話,筆者覺得只是返回一個Result類。讓我們看下面一段代碼。就知道筆者為什么會有這樣子的感覺。

1            // now execute the result, if we're supposed to
2                 if (proxy.getExecuteResult()) {
3                     executeResult();
4                 }           

上面這段代碼是在DefaultActionInvocation類的invoke方法的后半部分。如果返回的結果是字符串,最后還是會根據字符串來獲得對應的Result類的實例。Result類的實例類卻很多。如ServletDispatcherResult類等。找到了Result類的實例就會去執行他本身的execute方法。我們可以在executeResult方法的源碼里面體現出來。看一下吧。

DefaultActionInvocation類:

 1 private void executeResult() throws Exception {
 2         result = createResult();
 3 
 4         String timerKey = "executeResult: " + getResultCode();
 5         try {
 6             UtilTimerStack.push(timerKey);
 7             if (result != null) {
 8                 result.execute(this);
 9             } else if (resultCode != null && !Action.NONE.equals(resultCode)) {
10                 throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
11                         + " and result " + getResultCode(), proxy.getConfig());
12             } else {
13                 if (LOG.isDebugEnabled()) {
14                     LOG.debug("No result returned for action {} at {}", getAction().getClass().getName(), proxy.getConfig().getLocation());
15                 }
16             }
17         } finally {
18             UtilTimerStack.pop(timerKey);
19         }
20     }

DefaultActionInvocation類:

 1 public Result createResult() throws Exception {
 2         LOG.trace("Creating result related to resultCode [{}]", resultCode);
 3 
 4         if (explicitResult != null) {
 5             Result ret = explicitResult;
 6             explicitResult = null;
 7 
 8             return ret;
 9         }
10         ActionConfig config = proxy.getConfig();
11         Map<String, ResultConfig> results = config.getResults();
12 
13         ResultConfig resultConfig = null;
14 
15         try {
16             resultConfig = results.get(resultCode);
17         } catch (NullPointerException e) {
18             LOG.debug("Got NPE trying to read result configuration for resultCode [{}]", resultCode);
19         }
20         
21         if (resultConfig == null) {
22             // If no result is found for the given resultCode, try to get a wildcard '*' match.
23             resultConfig = results.get("*");
24         }
25 
26         if (resultConfig != null) {
27             try {
28                 return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
29             } catch (Exception e) {
30                 LOG.error("There was an exception while instantiating the result of type {}", resultConfig.getClassName(), e);
31                 throw new XWorkException(e, resultConfig);
32             }
33         } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
34             return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
35         }
36         return null;
37     }

前一段代碼:講述executeResult方法。executeResult方法只是用於執行Result類的實例。即是執行Result類的execute方法。

后一段代碼:進述executeResult方法里面用到的createResult方法。也就是筆者前面講到的會根據返回的結果字符串來獲得對應的Result類的實例。我們可以看到如果他返回是一個Result類的話,就直接返回。如果不是,就是從配置信息里面獲得對應在的result元素節點信息。通過result元素節點信息來生成對應的Result類的實例。顯然我們可以看到ObjectFactory類又在一次發揮了作用。在前面action類實例也是靠他來完成的。

到這里,相信有部分讀者會跟筆者一樣子有一個疑問?Result類到底是什么東東。其實他是用於對action類實例執行之后結果的處理。簡單點講可以理解為處理網頁。結果也有了。接下來便是返回結果給用戶顯示出來。我們也看到了在這個過程中DefaultActionInvocation類實現做了很多的工作。包括攔截器的調用。返回Result類的處理。不得說DefaultActionInvocation類真的很重要。

本章總結

本章主要是進述DefaultActionInvocation類執行action的相關內容。讓我們知道了是如何進行的執行action。其中用到ONGL相關的語法。也知道了action類實例執行之后。還會有對應的結果處理。當然這些用是交給於Result類的實例。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM