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类:
public String invokeActionOnly() throws Exception {
return invokeAction(getAction(), proxy.getConfig());
}
代码里面getAction方法就是获得action类实例。即是跟《Struts2 源码分析——Action代理类的工作》章节里面讲到的createAction方法有关。当程序执行到这里的时候,createAction方法已经新建好了action类实例。不清楚读者请到这章去看一下。让我们看一下invokeAction方法的源码吧。
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
String methodName = proxy.getMethod();//获得要执行的方法名。 LOG.debug("Executing action method = {}", methodName); String timerKey = "invokeAction: " + proxy.getActionName();
try {
UtilTimerStack.push(timerKey); Object methodResult;
try {
methodResult = ognlUtil.callMethod(methodName + "()", getStack().getContext(), action);//执行action类实例
} catch (MethodFailedException e) {
// if reason is missing method, try checking UnknownHandlers
if (e.getReason() instanceof NoSuchMethodException) {
if (unknownHandlerManager.hasUnknownHandlers()) {
try {
methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
} catch (NoSuchMethodException ignore) {
// throw the original one
throw e;
}
} else {
// throw the original one
throw e;
}
// throw the original exception as UnknownHandlers weren't able to handle invocation as well
if (methodResult == null) {
throw e;
}
} else {
// exception isn't related to missing action method, throw it
throw e;
}
}
return saveResult(actionConfig, methodResult);
} catch (NoSuchPropertyException e) {
throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
} catch (MethodFailedException e) {
// We try to return the source exception.
Throwable t = e.getCause(); if (actionEventListener != null) {
String result = actionEventListener.handleException(t, getStack());
if (result != null) {
return result;
}
}
if (t instanceof Exception) {
throw (Exception) t;
} else {
throw e;
}
} finally {
UtilTimerStack.pop(timerKey);
}
}
这个方法的做的事情是很简单。就是获得当前action类实例要执行的方法名。在根据OgnlUtil工具类在执行对应action类实例的方法。显然想要知道更深一点的话就必须深入OgnlUtil工具类的源码。让我们看一下吧。
OgnlUtil类:
public Object callMethod(final String name, final Map<String, Object> context, final Object root) throws OgnlException {
return compileAndExecuteMethod(name, context, new OgnlTask<Object>() {
public Object execute(Object tree) throws OgnlException {
return Ognl.getValue(tree, context, root);
}
});
}
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类型并返回。代码如下
protected String saveResult(ActionConfig actionConfig, Object methodResult) {
if (methodResult instanceof Result) {
this.explicitResult = (Result) methodResult; // Wire the result automatically
container.inject(explicitResult);
return null;
} else {
return (String) methodResult;
}
}
如果笔者没有查看源码的话,就不会知道原来action类实例在执行结束之后不只是一个String类型。还有一个叫Result类。如果有硬的讲的话,笔者觉得只是返回一个Result类。让我们看下面一段代码。就知道笔者为什么会有这样子的感觉。
// now execute the result, if we're supposed to
if (proxy.getExecuteResult()) {
executeResult();
}
上面这段代码是在DefaultActionInvocation类的invoke方法的后半部分。如果返回的结果是字符串,最后还是会根据字符串来获得对应的Result类的实例。Result类的实例类却很多。如ServletDispatcherResult类等。找到了Result类的实例就会去执行他本身的execute方法。我们可以在executeResult方法的源码里面体现出来。看一下吧。
DefaultActionInvocation类:
private void executeResult() throws Exception {
result = createResult(); String timerKey = "executeResult: " + getResultCode();
try {
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 }
} finally {
UtilTimerStack.pop(timerKey);
}
}
DefaultActionInvocation类:
public Result createResult() throws Exception {
LOG.trace("Creating result related to resultCode [{}]", resultCode); if (explicitResult != null) {
Result ret = explicitResult;
explicitResult = null; return ret;
}
ActionConfig config = proxy.getConfig();
Map<String, ResultConfig> results = config.getResults(); ResultConfig resultConfig = null; try {
resultConfig = results.get(resultCode);
} catch (NullPointerException e) {
LOG.debug("Got NPE trying to read result configuration for resultCode [{}]", resultCode);
} if (resultConfig == null) {
// If no result is found for the given resultCode, try to get a wildcard '*' match.
resultConfig = results.get("*");
} if (resultConfig != null) {
try {
return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
} catch (Exception e) {
LOG.error("There was an exception while instantiating the result of type {}", resultConfig.getClassName(), e);
throw new XWorkException(e, resultConfig);
}
} else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
}
return null;
}
前一段代码:讲述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的更多相关文章
- Struts2 源码分析——Result类实例
本章简言 上一章笔者讲到关于DefaultActionInvocation类执行action的相关知识.我们清楚的知道在执行action类实例之后会相关处理返回的结果.而这章笔者将对处理结果相关的内容 ...
- Struts2 源码分析——Action代理类的工作
章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息.相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识.而本章将讲到关于struts2启动成功 ...
- Struts2 源码分析——调结者(Dispatcher)之执行action
章节简言 上一章笔者写关于Dispatcher类如何处理接受来的request请求.当然读者们也知道他并非正真的执行action操作.他只是在执行action操作之前的准备工作.那么谁才是正真的执行a ...
- Struts2 源码分析——拦截器的机制
本章简言 上一章讲到关于action代理类的工作.即是如何去找对应的action配置信息,并执行action类的实例.而这一章笔者将讲到在执行action需要用到的拦截器.为什么要讲拦截器呢?可以这样 ...
- Struts2 源码分析-----工作原理分析
请求过程 struts2 架构图如下图所示: 依照上图,我们可以看出一个请求在struts的处理大概有如下步骤: 1.客户端初始化一个指向Servlet容器(例如Tomcat)的请求: 2.这个请求经 ...
- Struts2 源码分析——配置管理之PackageProvider接口
本章简言 上一章讲到关于ContainerProvider的知识.让我们知道struts2是如何注册相关的数据.也知道如何加载相关的配置信息.本章笔者将讲到如何加载配置文件里面的package元素节点 ...
- Struts2 源码分析——配置管理之ContainerProvider接口
本章简言 上一章笔者讲到关于Dispatcher类的执行action功能,知道了关于执行action需要用到的信息.而本章将会讲到的内容也跟Dispatcher类有关系.那就是配置管理中的Contai ...
- Struts2 源码分析——过滤器(Filter)
章节简言 上一章笔者试着建一个Hello world的例子.是一个空白的struts2例子.明白了运行struts2至少需要用到哪一些Jar包.而这一章笔者将根据前面章节(Struts2 源码分析—— ...
- Struts2 源码分析——Hello world
新建第一个应用程序 上一章我们讲到了关于struts2核心机制.对于程序员来讲比较概念的一章.而本章笔者将会亲手写一个Hello world的例子.所以如果对struts2使用比较了解的朋友,请跳过本 ...
随机推荐
- SQLServer存储过程事务用法
更多资源:http://denghejun.github.io IF object_id('InsertAntennaProcedure') IS NOT NULL DROP PROCEDURE In ...
- Concurrency并发性
今天看了有关性能的文章,性能也是客户所看重的. 文章推荐看了软件编程并发性. 就按书上敲了网址看:http://www.gotw.ca/publications/concurrency-ddj.htm ...
- Android安全开发之ZIP文件目录遍历
1.ZIP文件目录遍历简介 因为ZIP压缩包文件中允许存在“../”的字符串,攻击者可以利用多个“../”在解压时改变ZIP包中某个文件的存放位置,覆盖掉应用原有的文件.如果被覆盖掉的文件是动态链接s ...
- 论如何在手机端web前端实现自定义原生控件的样式
手机开发webapp的同学一定遇到过这样问题,如何为丑极了的手机元素应用自定义的样式.首先,要弄清楚为什么要定义手机原生控件的样式,就需要看看手机的那些原生框样式的丑陋摸样: android: ios ...
- NodeJS写个爬虫,把文章放到kindle中阅读
这两天看了好几篇不错的文章,有的时候想把好的文章 down 下来放到 kindle 上看,便写了个爬虫脚本,因为最近都在搞 node,所以就很自然的选择 node 来爬咯- 本文地址:http://w ...
- [nRF51822] 15、穿戴式设备上电量检测装置的设计及细节技术点(偏专业硬件文章)
穿戴式 设备如智能手环.智能手表一般采用几百毫安时的锂离子电池来供电.因此,与之配套的充电电路.稳压电路和电池电量检测电路便必不可少!本文主要谈谈该类消费类电子内部电池电量检测的一般方法及其优缺点. ...
- 浅析MSIL中间语言——PE文件结构篇
一.开篇 开篇我想讲一下于本文无关的话题,其实我很想美化一下自己博客园一直没时间弄,无意间找了博客园李宝亨的博客园里面有一篇分享自己主题的文章,我就将这个模板暂时用作我的blog主题,我要讲述一个关于 ...
- Azure Blob Storage 基本用法 -- Azure Storage 之 Blob
Azure Storage 是微软 Azure 云提供的云端存储解决方案,当前支持的存储类型有 Blob.Queue.File 和 Table. 笔者在<Azure Table storage ...
- 【原】通过JS打开IE新tab(非Window)的解决方案
近日项目里遇到限定在IE的tab窗口里打开新窗口的需求,结合网上的资源和亲自实践,总结以下比较可行的解决方案. 1.首先必须保证IE的设置正确.打开IE的Internet options ->G ...
- Android开发学习之路-回调机制学习笔记
不知道是我学Java的时候没有认真听还是怎么的,曾经一直不知道什么是“回调”,它有什么用,百度一大堆,都太复杂看不明白(好吧是我笨),所以想把自己理解的分享给其他看到的人,大家都真正认识一下这个重要的 ...