Structs ActionProxy深度阅读

ActionProxy是Action的一个代理类,也就是说Action的调用是通过ActionProxy实现的,其实就是调用了ActionProxy.execute()方法,而该方法又调用了ActionInvocation.invoke()方法。归根到底,最后调用的是DefaultActionInvocation.invokeAction()方法。

DefaultActionInvocation()->init()->createAction()。

最后通过调用ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction()

这里的步骤是先由ActionProxyFactory创建ActionInvocation和ActionProxy.

Java代码  

  1. public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
  2. ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
  3. container.inject(inv);
  4. return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
  5. }

下面先看DefaultActionInvocation的init方法

Java代码  

  1. public void init(ActionProxy proxy) {
  2. this.proxy = proxy;
  3. Map<String, Object> contextMap = createContextMap();
  4. // Setting this so that other classes, like object factories, can use the ActionProxy and other
  5. // contextual information to operate
  6. ActionContext actionContext = ActionContext.getContext();
  7. if (actionContext != null) {
  8. actionContext.setActionInvocation(this);
  9. }
  10. //创建Action,struts2中每一个Request都会创建一个新的Action
  11. createAction(contextMap);
  12. if (pushAction) {
  13. stack.push(action);
  14. contextMap.put("action", action);
  15. }
  16. invocationContext = new ActionContext(contextMap);
  17. invocationContext.setName(proxy.getActionName());
  18. // get a new List so we don‘t get problems with the iterator if someone changes the list
  19. List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
  20. interceptors = interceptorList.iterator();
  21. }
  22. protected void createAction(Map<String, Object> contextMap) {
  23. // load action
  24. String timerKey = "actionCreate: " + proxy.getActionName();
  25. try {
  26. UtilTimerStack.push(timerKey);
  27. //默认为SpringObjectFactory:struts.objectFactory=spring.这里非常巧妙,在struts.properties中可以重写这个属性
  28. //在前面BeanSelectionProvider中通过配置文件为ObjectFactory设置实现类
  29. //这里以Spring为例,这里会调到SpringObjectFactory的buildBean方法,可以通过ApplicationContext的getBean()方法得到Spring的Bean
  30. action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
  31. } catch (InstantiationException e) {
  32. throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());
  33. } catch (IllegalAccessException e) {
  34. throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());
  35. } catch (Exception e) {
  36. ...
  37. } finally {
  38. UtilTimerStack.pop(timerKey);
  39. }
  40. if (actionEventListener != null) {
  41. action = actionEventListener.prepare(action, stack);
  42. }
  43. }
  44. //SpringObjectFactory
  45. public Object buildBean(String beanName, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
  46. Object o = null;
  47. try {
  48. //SpringObjectFactory会通过web.xml中的context-param:contextConfigLocation自动注入ClassPathXmlApplicationContext
  49. o = appContext.getBean(beanName);
  50. } catch (NoSuchBeanDefinitionException e) {
  51. Class beanClazz = getClassInstance(beanName);
  52. o = buildBean(beanClazz, extraContext);
  53. }
  54. if (injectInternal) {
  55. injectInternalBeans(o);
  56. }
  57. return o;
  58. }

Java代码  

  1. //接下来看看DefaultActionInvocation 的invoke方法
  2. public String invoke() throws Exception {
  3. String profileKey = "invoke: ";
  4. try {
  5. UtilTimerStack.push(profileKey);
  6. if (executed) {
  7. throw new IllegalStateException("Action has already executed");
  8. }
  9. //递归执行interceptor
  10. if (interceptors.hasNext()) {
  11. //interceptors是InterceptorMapping实际上是像一个像FilterChain一样的Interceptor链
  12. //通过调用Invocation.invoke()实现递归牡循环
  13. final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
  14. String interceptorMsg = "interceptor: " + interceptor.getName();
  15. UtilTimerStack.push(interceptorMsg);
  16. try {
  17. //在每个Interceptor的方法中都会return invocation.invoke()
  18. resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
  19. }
  20. finally {
  21. UtilTimerStack.pop(interceptorMsg);
  22. }
  23. } else {
  24. //当所有interceptor都执行完,最后执行Action,invokeActionOnly会调用invokeAction()方法
  25. resultCode = invokeActionOnly();
  26. }
  27. // this is needed because the result will be executed, then control will return to the Interceptor, which will
  28. // return above and flow through again
  29. //在Result返回之前调用preResultListeners
  30. //通过executed控制,只执行一次
  31. if (!executed) {
  32. if (preResultListeners != null) {
  33. for (Object preResultListener : preResultListeners) {
  34. PreResultListener listener = (PreResultListener) preResultListener;
  35. String _profileKey = "preResultListener: ";
  36. try {
  37. UtilTimerStack.push(_profileKey);
  38. listener.beforeResult(this, resultCode);
  39. }
  40. finally {
  41. UtilTimerStack.pop(_profileKey);
  42. }
  43. }
  44. }
  45. // now execute the result, if we‘re supposed to
  46. //执行Result
  47. if (proxy.getExecuteResult()) {
  48. executeResult();
  49. }
  50. executed = true;
  51. }
  52. return resultCode;
  53. }
  54. finally {
  55. UtilTimerStack.pop(profileKey);
  56. }
  57. }
  58. //invokeAction
  59. protected String invokeAction(Object action,ActionConfig actionConfig)throws Exception{
  60. String methodName = proxy.getMethod();
  61. String timerKey = "invokeAction: " + proxy.getActionName();
  62. try {
  63. UtilTimerStack.push(timerKey);
  64. boolean methodCalled = false;
  65. Object methodResult = null;
  66. Method method = null;
  67. try {
  68. //java反射机制得到要执行的方法
  69. method = getAction().getClass().getMethod(methodName, new Class[0]);
  70. } catch (NoSuchMethodException e) {
  71. // hmm -- OK, try doXxx instead
  72. //如果没有对应的方法,则使用do+Xxxx来再次获得方法
  73. try {
  74. String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
  75. method = getAction().getClass().getMethod(altMethodName, new Class[0]);
  76. } catch (NoSuchMethodException e1) {
  77. // well, give the unknown handler a shot
  78. if (unknownHandlerManager.hasUnknownHandlers()) {
  79. try {
  80. methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
  81. methodCalled = true;
  82. } catch (NoSuchMethodException e2) {
  83. // throw the original one
  84. throw e;
  85. }
  86. } else {
  87. throw e;
  88. }
  89. }
  90. }
  91. //执行Method
  92. if (!methodCalled) {
  93. methodResult = method.invoke(action, new Object[0]);
  94. }
  95. //从这里可以看出可以Action的方法可以返回String去匹配Result,也可以直接返回Result类
  96. if (methodResult instanceof Result) {
  97. this.explicitResult = (Result) methodResult;
  98. // Wire the result automatically
  99. container.inject(explicitResult);
  100. return null;
  101. } else {
  102. return (String) methodResult;
  103. }
  104. } catch (NoSuchMethodException e) {
  105. throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
  106. } catch (InvocationTargetException e) {
  107. // We try to return the source exception.
  108. Throwable t = e.getTargetException();
  109. if (actionEventListener != null) {
  110. String result = actionEventListener.handleException(t, getStack());
  111. if (result != null) {
  112. return result;
  113. }
  114. }
  115. if (t instanceof Exception) {
  116. throw (Exception) t;
  117. } else {
  118. throw e;
  119. }
  120. } finally {
  121. UtilTimerStack.pop(timerKey);
  122. }
  123. }

action执行完了,还要根据ResultConfig返回到view,也就是在invoke方法中调用executeResult方法。

Java代码  

  1. private void executeResult() throws Exception {
  2. //根据ResultConfig创建Result
  3. result = createResult();
  4. String timerKey = "executeResult: " + getResultCode();
  5. try {
  6. UtilTimerStack.push(timerKey);
  7. if (result != null) {
  8. //开始执行Result,
  9. //可以参考Result的实现,如用了比较多的ServletDispatcherResult,ServletActionRedirectResult,ServletRedirectResult
  10. result.execute(this);
  11. } else if (resultCode != null && !Action.NONE.equals(resultCode)) {
  12. throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
  13. + " and result " + getResultCode(), proxy.getConfig());
  14. } else {
  15. if (LOG.isDebugEnabled()) {
  16. LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());
  17. }
  18. }
  19. } finally {
  20. UtilTimerStack.pop(timerKey);
  21. }
  22. }
  23. public Result createResult() throws Exception {
  24. //如果Action中直接返回的Result类型,在invokeAction()保存在explicitResult
  25. if (explicitResult != null) {
  26. Result ret = explicitResult;
  27. explicitResult = null;
  28. return ret;
  29. }
  30. //返回的是String则从config中得到当前Action的Results列表
  31. ActionConfig config = proxy.getConfig();
  32. Map<String, ResultConfig> results = config.getResults();
  33. ResultConfig resultConfig = null;
  34. synchronized (config) {
  35. try {
  36. //通过返回的String来匹配resultConfig
  37. resultConfig = results.get(resultCode);
  38. } catch (NullPointerException e) {
  39. // swallow
  40. }
  41. if (resultConfig == null) {
  42. // If no result is found for the given resultCode, try to get a wildcard ‘*‘ match.
  43. //如果找不到对应name的ResultConfig,则使用name为*的Result
  44. //说明可以用*通配所有的Result
  45. resultConfig = results.get("*");
  46. }
  47. }
  48. if (resultConfig != null) {
  49. try {
  50. //创建Result
  51. return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
  52. } catch (Exception e) {
  53. LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
  54. throw new XWorkException(e, resultConfig);
  55. }
  56. } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
  57. return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
  58. }
  59. return null;
  60. }
  61. public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
  62. String resultClassName = resultConfig.getClassName();
  63. Result result = null;
  64. if (resultClassName != null) {
  65. //buildBean中会用反射机制Class.newInstance来创建bean
  66. result = (Result) buildBean(resultClassName, extraContext);
  67. Map<String, String> params = resultConfig.getParams();
  68. if (params != null) {
  69. for (Map.Entry<String, String> paramEntry : params.entrySet()) {
  70. try {
  71. //reflectionProvider参见OgnlReflectionProvider;
  72. //resultConfig.getParams()就是result配置文件里所配置的参数<param></param>
  73. //setProperties方法最终调用的是Ognl类的setValue方法
  74. //这句其实就是把param名值设置到根对象result上
  75. reflectionProvider.setProperty(paramEntry.getKey(), paramEntry.getValue(), result, extraContext, true);
  76. } catch (ReflectionException ex) {
  77. if (LOG.isErrorEnabled())
  78. LOG.error("Unable to set parameter [#0] in result of type [#1]", ex,
  79. paramEntry.getKey(), resultConfig.getClassName());
  80. if (result instanceof ReflectionExceptionHandler) {
  81. ((ReflectionExceptionHandler) result).handle(ex);
  82. }
  83. }
  84. }
  85. }
  86. }
  87. return result;
  88. }
时间: 2024-08-10 13:25:44

Structs ActionProxy深度阅读的相关文章

《从0到1》深度阅读笔记zz

没有人能精准地预测未来,我们只知道两件事:一是世界必然会变得不同:二是现在再好的描述也不能让我们看到清晰的未来. 创业者把成就归功于商业模式和机会窗口,归功于创业者本人拥有的资源和能力,但还有一个最重要的因素,就是对未来有宏观的判断,从未来着手当下,最终走向成功. <从 0到1>不是一本成功学的书,也不是创业指导手册,而是一本关于创造和走向未来的书,一本思维运动的书.没有人能手把手教你创新,教你创造出不一样的东 西,实现从0到1的突破.即便并非创业者,我同样诚恳地推荐这本书,因为“最反主流的行

技术文章的阅读姿势

阅读技术文章可以说是我们程序员的日常之一,Peak 君每天也会进行定量的阅读.特写一篇小文分享下心得,介绍下过去几年,在纠正阅读习惯上所做的一些努力和取得的成果,或许可以帮助一些朋友,节省少许阅读时间,提升一点学习效率. 差不多两年前,我开始搭建 Android 相关的知识体系.最开始的想法是从基础知识的积累开始,正好这几年社区的技术分享盛行,「掘金」.「开发者头条」.「简书」等渠道上每天都有大量的新文章发布,文章主题五花八门,内容深浅不一,看上去都很不错.可坚持读了几天之后,深感自己踏进了错误

(13)碎片化阅读只会让你变得越来越愚蠢

碎片化阅读正在令你变得越来越愚蠢 昨天下午我坐在回上海的火车上,掏出Kindle准备看<金字塔原理>. 当我开始看这本书的时候有两件非常可怕的事情发生了. 第一件事,我发现自己的大脑竟然像转不动了一样! 这本书的内容并不算过于的晦涩,但我理解起来非常的吃力,有些句子反复读了两三次仍然理解不了,仍然记不住. 可以明显的感觉到,大脑里负责进行复杂阅读和理解的部分像锈住了一样,不管我多么努力,这部分始终转不起来. 第二件事,我发现自己的注意力涣散的可怕! 看了两三段之后不由自主的就跑神了,等我回过神

技术文章首选官方重精读不泛读,求阅读质量而非数量---转自peak

阅读技术文章可以说是我们程序员的日常之一,Peak 君每天也会进行定量的阅读.特写一篇小文分享下心得,介绍下过去几年,在纠正阅读习惯上所做的一些努力和取得的成果,或许可以帮助一些朋友,节省少许阅读时间,提升一点学习效率. 差不多两年前,我开始搭建 Android 相关的知识体系.最开始的想法是从基础知识的积累开始,正好这几年社区的技术分享盛行,「掘金」.「开发者头条」.「简书」等渠道上每天都有大量的新文章发布,文章主题五花八门,内容深浅不一,看上去都很不错.可坚持读了几天之后,深感自己踏进了错误

时间太少,如何阅读?

你有阅读的习惯吗?有自己的阅读框架吗? ... 国庆长假,没有到处跑,闲在家里读读书.看了一下我在豆瓣标记为 "想读" 的书籍已经突破了 300 本,而已标记读过的书才一百多本,感觉是永远读不完了. 好早以前我这个 "想读" 列表是很短的,一般不超过 20 本,因为以前我看见这个列表太长了后,就会主动停止往里面再添加了,直到把它们读完了,这样倒是有助于缓解下这种读不完的压力与焦虑感. 但后来渐渐想明白这个方法其实有很大的弊端,因为这样的处理算法是先进先出的,而更好的

微信时代:“脑”会被数字化吗?

微信时代:"阅读脑"会被数字化吗? --世界读书日专家谈浅阅读的喜与忧 本报记者 杨 纯 4月23日是世界读书日.阅读有多可贵?公元前4世纪米南德说,"喜欢阅读的人,就像拥有两个人生". 然而,第十三次全国国民阅读调查数据显示,2015年我国成年国民数字化阅读方式的接触率连续七年持续上升,首次超过60.0%.尤其微信阅读飞速增长,超半数国民进行微信阅读,刷朋友圈.看公众号已成为大众生活的一部分. 数字化阅读趋势难逆转 国民阅读研究与促进中心主任徐升国接受科技日报记者

刷朋友圈真的会令你变蠢吗?

一直听说有过度沉浸互联网令人变笨的说法."刷朋友圈太浪费时间了","朋友圈让我们不能静心读书".我周围一些朋友平均每天泡在朋友圈的时间,多达数小时,刷帖子的.卖面膜的.刷自拍的.卖萌的.点赞的等等,朋友圈让我们的信息接受更加碎片化.朋友圈,真的会让人变愚蠢吗? 工具不仅使得我们获得便利,更多是让你改变看待世界的方式,工具会让我们的意识不断进化.令你变蠢的,未必是工具,而是信息接受模式.我们面对信息环境,早已悄然变换. 且看如下场景: 场景1:你在课堂上遇到疑惑,老师

专注做事,竟然也成为我们的稀缺能力 [20160310]

互联网正在不知不觉中改造着我们的大脑,使我们失去深度阅读和缜密思维的能力.我们的注意力如今如此分散支离——专注做事,有时竟已成为了一种稀缺能力.然而不管对工作还是生活,专注都是一项不可或缺的能力. Oliver Emberton将我们的大脑比作就像灌满了蜜蜂的沙滩排球,上百个互相矛盾的冲动想法把我们推向不同的方向. 于是,通常的情况是那个球那儿也到不了.它更容易受地形的影响,而不是受到“蜜蜂”们的控制.这是绝大多数人的生活方式. 偏执狂一般聚焦在一个目标上,这可能是能够成功的唯一策略.当你能够在

Unix时间戳

什么是Unix时间戳? Unix时间戳(英文为Unix epoch, Unix time, POSIX time 或 Unix timestamp) 是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒. 注: javascript:Math.round(new Date().getTime()/1000),getTime()返回数值的单位是毫秒 C#:DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) /