本章简言

上一章讲到关于拦截器的机制的知识点,让我们对拦截器有了一定的认识。我们也清楚的知道在执行用户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类:

  1. private <T> Object compileAndExecuteMethod(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
  2. Object tree;
  3. if (enableExpressionCache) {
  4. tree = expressions.get(expression);
  5. if (tree == null) {
  6. tree = Ognl.parseExpression(expression);
  7. checkSimpleMethod(tree, context);
  8. }
  9. } else {
  10. tree = Ognl.parseExpression(expression);
  11. checkSimpleMethod(tree, context);
  12. }
  13.  
  14. final T exec = task.execute(tree);
  15. // if cache is enabled and it's a valid expression, puts it in
  16. if(enableExpressionCache) {
  17. expressions.putIfAbsent(expression, tree);
  18. }
  19. return exec;
  20. }

笔者看到这里的时候就有一点心烦。主要是一看就知道又要去学习一下关于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. 7 if (result != null) {
  8. 8 result.execute(this);
  9. 9 } else if (resultCode != null && !Action.NONE.equals(resultCode)) {
  10. 10 throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
  11. 11 + " and result " + getResultCode(), proxy.getConfig());
  12. 12 } else {
  13. 13 if (LOG.isDebugEnabled()) {
  14. 14 LOG.debug("No result returned for action {} at {}", getAction().getClass().getName(), proxy.getConfig().getLocation());
  15. 15 }
  16. 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类的实例。

Struts2 源码分析——DefaultActionInvocation类的执行action的更多相关文章

  1. Struts2 源码分析——Result类实例

    本章简言 上一章笔者讲到关于DefaultActionInvocation类执行action的相关知识.我们清楚的知道在执行action类实例之后会相关处理返回的结果.而这章笔者将对处理结果相关的内容 ...

  2. Struts2 源码分析——Action代理类的工作

    章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息.相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识.而本章将讲到关于struts2启动成功 ...

  3. Struts2 源码分析——调结者(Dispatcher)之执行action

    章节简言 上一章笔者写关于Dispatcher类如何处理接受来的request请求.当然读者们也知道他并非正真的执行action操作.他只是在执行action操作之前的准备工作.那么谁才是正真的执行a ...

  4. Struts2 源码分析——拦截器的机制

    本章简言 上一章讲到关于action代理类的工作.即是如何去找对应的action配置信息,并执行action类的实例.而这一章笔者将讲到在执行action需要用到的拦截器.为什么要讲拦截器呢?可以这样 ...

  5. Struts2 源码分析-----工作原理分析

    请求过程 struts2 架构图如下图所示: 依照上图,我们可以看出一个请求在struts的处理大概有如下步骤: 1.客户端初始化一个指向Servlet容器(例如Tomcat)的请求: 2.这个请求经 ...

  6. Struts2 源码分析——配置管理之PackageProvider接口

    本章简言 上一章讲到关于ContainerProvider的知识.让我们知道struts2是如何注册相关的数据.也知道如何加载相关的配置信息.本章笔者将讲到如何加载配置文件里面的package元素节点 ...

  7. Struts2 源码分析——配置管理之ContainerProvider接口

    本章简言 上一章笔者讲到关于Dispatcher类的执行action功能,知道了关于执行action需要用到的信息.而本章将会讲到的内容也跟Dispatcher类有关系.那就是配置管理中的Contai ...

  8. Struts2 源码分析——过滤器(Filter)

    章节简言 上一章笔者试着建一个Hello world的例子.是一个空白的struts2例子.明白了运行struts2至少需要用到哪一些Jar包.而这一章笔者将根据前面章节(Struts2 源码分析—— ...

  9. Struts2 源码分析——Hello world

    新建第一个应用程序 上一章我们讲到了关于struts2核心机制.对于程序员来讲比较概念的一章.而本章笔者将会亲手写一个Hello world的例子.所以如果对struts2使用比较了解的朋友,请跳过本 ...

随机推荐

  1. JavaScript判断移动端及pc端访问不同的网站

    JavaScript判断移动端及pc端访问不同的网站 现在很多网站都是分为两个版本,一个pc端的一个移动端的(响应式除外),针对这两个版本,就需要对访问的设备进行判断,如果是pc,就直接访问pc网站, ...

  2. IDT HOOK思路整理

    IDT(中断描述符表)分为IRQ(真正的硬件中断)和软件中断(又叫异常). HOOK的思路为,替换键盘中断处理的函数地址为自己的函数地址.这样在键盘驱动和过滤驱动之前就可以截获键盘输入. 思路确定之后 ...

  3. 结合数据库登录注册模块,登录成功之后跳到WebView

    最近刚刚做了一个模块,在本地建立一个数据库,存储注册的账号,登录的时候取出,正确则登录,登录之后跳到一个webView网页. 直接上代码吧. LoginActivity.java package co ...

  4. 已经过事务处理的 MSMQ 绑定(转载)

    https://msdn.microsoft.com/zh-cn/biztalk/ms751493 本示例演示如何使用消息队列 (MSMQ) 执行已经过事务处理的排队通信. 注意 本主题的末尾介绍了此 ...

  5. 【直播】APP全量混淆和瘦身技术揭秘

    [直播]APP全量混淆和瘦身技术揭秘 近些年来移动APP数量呈现爆炸式的增长,黑产也从原来的PC端转移到了移动端,通过逆向手段造成数据泄漏.源码被盗.APP被山寨.破解后注入病毒或广告现象让用户苦不堪 ...

  6. 给Mac OS X的“逻辑宗卷组”改名

    近日更新了Mac OS X的10.10版本——Yosemite,感觉良好,但当我在打开磁盘工具的时候发现了一个美中不足的地方,磁盘宗卷组的名字居然还是我之前的“Mavericks”,如图: 如何对它进 ...

  7. EasyPR--开发详解(3)高斯模糊、灰度化和Sobel算子

    在上篇文章中我们了解了PlateLocate的过程中的所有步骤.在本篇文章中我们对前3个步骤,分别是高斯模糊.灰度化和Sobel算子进行分析. 一.高斯模糊 1.目标 对图像去噪,为边缘检测算法做准备 ...

  8. 小型单文件NoSQL数据库SharpFileDB初步实现

    小型单文件NoSQL数据库SharpFileDB初步实现 我不是数据库方面的专家,不过还是想做一个小型的数据库,算是一种通过mission impossible进行学习锻炼的方式.我知道这是自不量力, ...

  9. 海淘手表Invicta8926OB到手~晒图

    3月3号通过国内代购网站Hai360海外购下单: 3月5号美亚发货: 3月6号到达转运仓: 3月12号到达天津清关: 清关等了7天: 3月19号转国内快递,我将原武汉地址,改上海,耽误了3天: 3月2 ...

  10. PMO是什么?如何与其他部门协作配合提高项目成功率?

    许多公司里,有许多IT项目,特别是在软件公司里,许多开发团队并没有运用灵敏开发来进行项目办理.在某些状况下,尤其在一些公司里IT不是很受注重的,只能作为一个事务支撑部分,灵敏团队面对的首要疑问,是缺少 ...