- 浏览: 108137 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
BlueSkator:
bo_hai 写道rz -y 强制覆盖linux上的文件。
linux rz sz 命令 -
bo_hai:
rz -y 强制覆盖linux上的文件。
linux rz sz 命令 -
yoyo837:
我以为是原生ajax
ajax 三种提交请求的方法 -
BlueSkator:
jahu 写道火狐,使用不成功,写js代码注意,兼容性噢 火狐 ...
js 预提交请求时确定后提交实现 -
jahu:
火狐,使用不成功,写js代码注意,兼容性
js 预提交请求时确定后提交实现
Dispatcher类
Dispatcher类是在struts2中定义的,在 webwork2 中叫DispatcherUtils,
作用相同:
1 初始化模块参数,
2 将 xwork 于 web层分离,
3 中转请求,进入xwork 的 action处理流程。
1 初始化,涉及方法 init()
在上一篇的 FilterDispatcher中的init() 方法,有这么两句:
就是调用Dispatcher的初始化。
2 xwork 定义了一个类 : ActonContext,
ActonContext 有两个作用
1) 线程安全,
过这个类,xwork2 实现了 action 的线程安全,
为每一次的action请求, 分配一个独立的ActonContext。
2) 于 web 层分离,
xwork的做法,是把web层中组件中的变量
(主要是javax.servlet包中的类,如HttpServletRequest)
取出来,放入ActonContext。
这样xwork就不要在向外访问web层了。
涉及方法
- public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
- ActionMapping mapping, ServletContext context) {
Dispatcher 的这个方法,把web层的参数取出然后放入Map 中,(以备xwork构造ActionContext)。
3 中转请求,调action处理逻辑,这个是比较重要的一个地方,
- public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
- ActionMapping mapping) throws ServletException {
- Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
- // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
- ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
- if (stack != null ) {
- extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));
- }
- String timerKey = "Handling request from Dispatcher" ;
- try {
- UtilTimerStack.push(timerKey);
- String namespace = mapping.getNamespace();
- String name = mapping.getName();
- String method = mapping.getMethod();
- Configuration config = configurationManager.getConfiguration();
- ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory. class ).createActionProxy(
- namespace, name, extraContext, true , false );
- proxy.setMethod(method);
- request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
- // if the ActionMapping says to go straight to a result, do it!
- if (mapping.getResult() != null ) {
- Result result = mapping.getResult();
- result.execute(proxy.getInvocation());
- } else {
- proxy.execute();
- }
- // If there was a previous value stack then set it back onto the request
- if (stack != null ) {
- request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
- }
- } catch (ConfigurationException e) {
- LOG.error( "Could not find action or result" , e);
- sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
- } catch (Exception e) {
- throw new ServletException(e);
- } finally {
- UtilTimerStack.pop(timerKey);
- }
- }
Configuration config = configurationManager.getConfiguration();
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, extraContext, true, false);
得到框架的配置信息,得到框架的Container,构造出 ActionProxy。
这个过程中,涉及到几个xwork较核心的类,Configuration, ConfigurationManager, Container,
Configuration, ConfigurationManager 负责框架配置信息初始化的类
- public class DefaultConfiguration implements Configuration {
- protected static final Logger LOG = LoggerFactory.getLogger(DefaultConfiguration. class );
- // Programmatic Action Configurations
- protected Map<String, PackageConfig> packageContexts = new LinkedHashMap<String, PackageConfig>();
- protected RuntimeConfiguration runtimeConfiguration;
- protected Container container;
- protected String defaultFrameworkBeanName;
- protected Set<String> loadedFileNames = new TreeSet<String>();
- ObjectFactory objectFactory;
这是描述框架总体配置信息的,其中,
Map<String, PackageConfig> 放的是package的config,对应struts2 配置文件中 package标签内容,
PackageConfig
- public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {
- private static final Logger LOG = LoggerFactory.getLogger(PackageConfig. class );
- private Map<String, ActionConfig> actionConfigs;
- private Map<String, ResultConfig> globalResultConfigs;
- private Map<String, Object> interceptorConfigs;
- private Map<String, ResultTypeConfig> resultTypeConfigs;
- private List<ExceptionMappingConfig> globalExceptionMappingConfigs;
- private List<PackageConfig> parents;
- private String defaultInterceptorRef;
- private String defaultActionRef;
- private String defaultResultType;
- private String defaultClassRef;
- private String name;
- private String namespace = "" ;
- private boolean isAbstract = false ;
- private boolean needsRefresh;
里边是一些package的信息,其中Map<String, ActionConfig> 放的就是action的描述类ActionConfig了
ActionConfig
- public class ActionConfig extends Located implements Serializable {
- public static final String WILDCARD = "*" ;
- protected List<InterceptorMapping> interceptors;
- protected Map<String, String> params;
- protected Map<String, ResultConfig> results;
- protected List<ExceptionMappingConfig> exceptionMappings;
- protected String className;
- protected String methodName;
- protected String packageName;
- protected String name;
- protected Set<String> allowedMethods;
其中
interceptors : action的拦截器,
results: action执行后用来收尾的,如,跳到定义好的页面。 className 类名,packageName 包名,methodName 默认是 execute。
框架一般有个必须要做的活,就是把某配置文件的信息读到框架系统中来,
xwork2中,XMLConfigurationProvider 就是干这个的,
他读xml配置文件(就是我们定义action的配置的那个文件),
然后把这些信息写到Configuration中。
ConfigurationManager 则负责调用适当的 ConfigurationProvider,
执行初始化逻辑,和返回具体的Configuration。
初始化等前期工作完毕后,则开始执行action的逻辑了,
xwork 使用了代理模式执行action,
proxy.execute();
从这一句起, strust2 就把工作交接给了xwork了。
接着看Dispatcher
- ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory. class ).createActionProxy(
- namespace, name, extraContext, true , false );
[java] view plain copy
- ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory. class ).createActionProxy(
- namespace, name, extraContext, true , false );
用Container 构造一个 ActionProxyFactory,在用ActionProxyFactory create一个 ActionProxy。
- public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext) {
- ActionInvocation inv = new DefaultActionInvocation(extraContext, true );
- container.inject(inv);
- return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
- }
[java] view plain copy
- public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext) {
- ActionInvocation inv = new DefaultActionInvocation(extraContext, true );
- container.inject(inv);
- return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
- }
xwork中,action的调用是借助 Proxy 模式,
Proxy 模式的实现,则是 DefaultActionProxy, Action, ActionInvocation 合伙完成的。
下边是一些关键代码
DefaultActionProxy
- public String execute() throws Exception {
- ActionContext nestedContext = ActionContext.getContext();
- ActionContext.setContext(invocation.getInvocationContext());
- String retCode = null ;
- String profileKey = "execute: " ;
- try {
- UtilTimerStack.push(profileKey);
- <SPAN style= "COLOR: #ff0000" > retCode = invocation.invoke();</SPAN>
- } finally {
- if (cleanupContext) {
- ActionContext.setContext(nestedContext);
- }
- UtilTimerStack.pop(profileKey);
- }
- return retCode;
- }
[java] view plain copy
- public String execute() throws Exception {
- ActionContext nestedContext = ActionContext.getContext();
- ActionContext.setContext(invocation.getInvocationContext());
- String retCode = null ;
- String profileKey = "execute: " ;
- try {
- UtilTimerStack.push(profileKey);
- <span style="color: #ff0000;" > retCode = invocation.invoke();</span>
- } finally {
- if (cleanupContext) {
- ActionContext.setContext(nestedContext);
- }
- UtilTimerStack.pop(profileKey);
- }
- return retCode;
- }
DefaultActionInvocation 得 invoke 方法。
- public String invoke() throws Exception {
- String profileKey = "invoke: " ;
- try {
- UtilTimerStack.push(profileKey);
- if (executed) {
- throw new IllegalStateException( "Action has already executed" );
- }
- if (interceptors.hasNext()) {
- final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
- UtilTimerStack.profile( "interceptor: " +interceptor.getName(),
- new UtilTimerStack.ProfilingBlock<String>() {
- public String doProfiling() throws Exception {
- <SPAN style= "COLOR: #ff0000" >resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation. this );</SPAN>
- return null ;
- }
- });
- } else {
- <SPAN style= "COLOR: #ff0000" >resultCode = invokeActionOnly();</SPAN>
- }
- // this is needed because the result will be executed, then control will return to the Interceptor, which will
- // return above and flow through again
- if (!executed) {
- if (preResultListeners != null ) {
- for (Iterator iterator = preResultListeners.iterator();
- iterator.hasNext();) {
- PreResultListener listener = (PreResultListener) iterator.next();
- String _profileKey= "preResultListener: " ;
- try {
- UtilTimerStack.push(_profileKey);
- listener.beforeResult( this , resultCode);
- }
- finally {
- UtilTimerStack.pop(_profileKey);
- }
- }
- }
- // now execute the result, if we're supposed to
- if (proxy.getExecuteResult()) {
- executeResult();
- }
- executed = true ;
- }
- return resultCode;
- }
- finally {
- UtilTimerStack.pop(profileKey);
- }
- }
[java] view plain copy
- public String invoke() throws Exception {
- String profileKey = "invoke: " ;
- try {
- UtilTimerStack.push(profileKey);
- if (executed) {
- throw new IllegalStateException( "Action has already executed" );
- }
- if (interceptors.hasNext()) {
- final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
- UtilTimerStack.profile("interceptor: " +interceptor.getName(),
- new UtilTimerStack.ProfilingBlock<String>() {
- public String doProfiling() throws Exception {
- <span style="color: #ff0000;" >resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation. this );</span>
- return null ;
- }
- });
- } else {
- <span style="color: #ff0000;" >resultCode = invokeActionOnly();</span>
- }
- // this is needed because the result will be executed, then control will return to the Interceptor, which will
- // return above and flow through again
- if (!executed) {
- if (preResultListeners != null ) {
- for (Iterator iterator = preResultListeners.iterator();
- iterator.hasNext();) {
- PreResultListener listener = (PreResultListener) iterator.next();
- String _profileKey="preResultListener: " ;
- try {
- UtilTimerStack.push(_profileKey);
- listener.beforeResult(this , resultCode);
- }
- finally {
- UtilTimerStack.pop(_profileKey);
- }
- }
- }
- // now execute the result, if we're supposed to
- if (proxy.getExecuteResult()) {
- executeResult();
- }
- executed = true ;
- }
- return resultCode;
- }
- finally {
- UtilTimerStack.pop(profileKey);
- }
- }
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); 是判断这个action是否有未被执行的拦截器,
resultCode = invokeActionOnly();
如没有或拦截器执行完毕,则执行invokeActionOnly
invokeActionOnly方法
- public String <SPAN style= "COLOR: #ff0000" >invokeActionOnly()</SPAN> throws Exception {
- return invokeAction(getAction(), proxy.getConfig());
- }
- rotected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
- String methodName = proxy.getMethod();
- if (LOG.isDebugEnabled()) {
- LOG.debug( "Executing action method = " + actionConfig.getMethodName());
- }
- String timerKey = "invokeAction: " +proxy.getActionName();
- try {
- UtilTimerStack.push(timerKey);
- boolean methodCalled = false ;
- Object methodResult = null ;
- Method method = null ;
- try {
- <SPAN style= "COLOR: #ff0000" >method = getAction().getClass().getMethod(methodName, new Class[ 0 ]);</SPAN>
- } catch (NoSuchMethodException e) {
- // hmm -- OK, try doXxx instead
- try {
- String altMethodName = "do" + methodName.substring( 0 , 1 ).toUpperCase() + methodName.substring( 1 );
- method = getAction().getClass().getMethod(altMethodName, new Class[ 0 ]);
- } catch (NoSuchMethodException e1) {
- // well, give the unknown handler a shot
- if (unknownHandler != null ) {
- try {
- methodResult = unknownHandler.handleUnknownActionMethod(action, methodName);
- methodCalled = true ;
- } catch (NoSuchMethodException e2) {
- // throw the original one
- throw e;
- }
- } else {
- throw e;
- }
- }
- }
- if (!methodCalled) {
- <SPAN style= "COLOR: #ff0000" >methodResult = method.invoke(action, new Object[ 0 ]);</SPAN>
- }
- if (methodResult instanceof Result) {
- this .explicitResult = (Result) methodResult;
- return null ;
- } else {
- return (String) methodResult;
- }
- } catch (NoSuchMethodException e) {
- throw new IllegalArgumentException( "The " + methodName + "() is not defined in action " + getAction().getClass() + "" );
- } catch (InvocationTargetException e) {
- // We try to return the source exception.
- Throwable t = e.getTargetException();
- 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);
- }
- }
[java] view plain copy
- public String <span style= "color: #ff0000;" >invokeActionOnly()</span> throws Exception {
- return invokeAction(getAction(), proxy.getConfig());
- }
- rotected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
- String methodName = proxy.getMethod();
- if (LOG.isDebugEnabled()) {
- LOG.debug("Executing action method = " + actionConfig.getMethodName());
- }
- String timerKey = "invokeAction: " +proxy.getActionName();
- try {
- UtilTimerStack.push(timerKey);
- boolean methodCalled = false ;
- Object methodResult = null ;
- Method method = null ;
- try {
- <span style="color: #ff0000;" >method = getAction().getClass().getMethod(methodName, new Class[ 0 ]);</span>
- } catch (NoSuchMethodException e) {
- // hmm -- OK, try doXxx instead
- try {
- String altMethodName = "do" + methodName.substring( 0 , 1 ).toUpperCase() + methodName.substring( 1 );
- method = getAction().getClass().getMethod(altMethodName, new Class[ 0 ]);
- } catch (NoSuchMethodException e1) {
- // well, give the unknown handler a shot
- if (unknownHandler != null ) {
- try {
- methodResult = unknownHandler.handleUnknownActionMethod(action, methodName);
- methodCalled = true ;
- } catch (NoSuchMethodException e2) {
- // throw the original one
- throw e;
- }
- } else {
- throw e;
- }
- }
- }
- if (!methodCalled) {
- <span style="color: #ff0000;" >methodResult = method.invoke(action, new Object[ 0 ]);</span>
- }
- if (methodResult instanceof Result) {
- this .explicitResult = (Result) methodResult;
- return null ;
- } else {
- return (String) methodResult;
- }
- } catch (NoSuchMethodException e) {
- throw new IllegalArgumentException( "The " + methodName + "() is not defined in action " + getAction().getClass() + "" );
- } catch (InvocationTargetException e) {
- // We try to return the source exception.
- Throwable t = e.getTargetException();
- 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);
- }
- }
method = getAction().getClass().getMethod(methodName, new Class[0]); 取出需执行的action的方法,如 execute()
methodResult = method.invoke(action, new Object[0]); 利用反射,执行action 方法
上边就是利用proxy模式,调用一个action的具体逻辑 的 过程。
一般的action里,都有一些变量,如从request中取出的参数等,
如要执行action 方法,如execute(),则必须先吧这些变量赋值 已供方法中使用,
这个工作,就是 拦截器(Interceptor) 的任务之一,
下篇探讨一下拦截器。
发表评论
-
检查maven 项目jar包依赖是否冲突
2015-07-23 16:16 6868在项目发布的时候,一般都需要进行依赖冲突检查或者重复类的检 ... -
java 类加载顺序
2015-05-19 12:02 685加载顺序是: ... -
springmvc 中restful 的url 可能出现的问题
2015-01-19 11:27 935< url-pattern > / </ u ... -
注册表无法访问 解决方案
2015-01-18 13:55 1082可以采用下面的两种方法来解决该问题: 1、用组策略解决该 ... -
像打开QQ和迅雷一样在网站中点击打开本地的应用
2015-01-07 15:08 1543例子: 说到单点登录,往往是和Portal(门户) ... -
登录验证码的实现
2014-09-22 11:32 661import java.awt.Color; impor ... -
获取前n天的日期
2014-09-22 11:20 663public static String getNextFe ... -
j2ee 导出excel ,poi + jxl
2014-09-22 11:11 14461、POI 导出 Excel public static ... -
ajax 三种提交请求的方法
2014-09-22 11:04 84681、ajax 提交请求 $.ajax({ typ ... -
用debug模式启动tomcat出现 classNotFound ,无法启动服务器
2014-08-13 20:06 802解决方案:点击线程右键选择Resume 选项,然后即可重启 -
数据库中无数据时查询数据为空,但出现空指针异常的解决方案
2014-07-24 16:15 8390异常:java.lang.NullPointerExcep ... -
Invoking validate() on action Struts2 的验证问题
2014-07-23 22:20 666问题描述: 出现找不到Action且ActionMetho ... -
日历插件的应用
2014-07-23 21:07 8491、拷贝js到相应的目录 2、引入js <scri ... -
tomcat 造成的乱码原因与解决方案
2014-07-15 20:08 777只需在tomcat/conf/server.xml 中增加一 ... -
js 预提交请求时确定后提交实现
2014-07-15 20:04 1303<!DOCTYPE html PUBLIC " ... -
powerdesigner的应用
2014-07-09 15:00 9611、powerdesigner 生成sql脚本中 生成列名 ... -
VC6.0下显示行号
2013-09-29 02:47 890VC6.0是一款比较稳定的功能强大的IDE,目前也有很多人在 ... -
js返回上一页的脚本
2012-04-19 18:52 1188返回上一页js代码 ... -
第一次做myeclipse6.5 + struts2遇到的问题
2012-02-20 20:46 1474由于myeclipse7.0没有集成struts2,只 ... -
Struts2中struts.xml的Action、namespace配置详解
2012-02-20 20:43 2375今天做了个struts2练习,发现自己对struts.xml的 ...
相关推荐
《Xwork2 源码阅读(一)——深度解析框架基础与核心机制》 Xwork2是Struts2框架的核心部分,它提供了一种基于Action的模型-视图-控制器(MVC)架构,是Java Web开发中的重要组件。这篇分析文章将深入探讨Xwork2的...
《Xwork2 源码阅读.pdf》是一个深入解析Xwork2框架的文档,结合了Webwork和Struts2的源码分析,旨在帮助开发者理解这两个著名MVC框架的内部工作机制。Xwork2是Struts2的核心部分,负责处理Action的执行流程、拦截器...
《深入解析xwork 2.x源码:从2.1.2到2.2.1的演进》 xwork作为一个强大的Action框架,是Struts2的...通过深入学习xwork源码,开发者可以更好地掌握MVC设计模式,理解Web应用的控制层实现,从而在实践中发挥更大的效能。
8. **设计模式**:XWork源码中运用了多种设计模式,如工厂模式(Factory)、单例模式(Singleton)、装饰器模式(Decorator)等,这些都是Java开发中的经典模式,值得深入研究。 通过学习和分析XWork 2.1.6的源码,...
在06170300180这个文件中,可能包含了Struts2和XWork2的源码,你可以通过阅读这些源码来进一步了解这两个框架的实现细节。这将是一个宝贵的资源,帮助你深入学习和掌握Struts2和XWork2,从而提升你的Java Web开发...
《深入理解XWork框架:官方源码解析》 XWork是一个强大的Action框架,它为Java Web应用程序提供了模型-视图-控制器(MVC)模式的支持。这个框架的主要目标是简化企业级应用的开发,提高代码的可维护性和可扩展性。...
这次我们讨论的是"xwork_struts2"源码,包括"struts2-core-2.3.1.2.jar"和"xwork-core-2.3.1.2.jar"两个部分。 1. **XWork核心**:XWork是Struts2的基础,它提供了一套动作框架,负责处理HTTP请求、执行业务逻辑...
深入学习XWork源码,你可以了解到以下核心部分: 1. **ActionInvocation**:这是执行Action的入口点,它维护了拦截器栈,并负责调用每个拦截器和Action。 2. **ActionProxy**:ActionProxy是ActionInvocation的...
深入理解XWork源码对于学习和优化Struts2应用至关重要。在这个压缩包中,包含的是XWork 2.0.7版本的源代码,特别适合开发者进行研究和调试。以下是关于XWork源码及如何在Eclipse中关联XWork源码的详细讲解。 1. **...
总之,这个"XWork源码及打包"资源包为深入学习和开发基于Struts2的Web应用提供了宝贵的材料,无论是初学者还是经验丰富的开发者,都能从中受益。通过研究源码,开发者不仅可以提高技能,还能更好地利用和定制这个...
XWork是Struts2框架的核心组件,它提供了一种基于拦截器(Interceptor)的Action管理机制,为构建可维护、可扩展的企业级Web应用程序...深入学习XWork源码,对于提升对Struts2乃至整个MVC框架的理解都是非常有益的。
Struts-xwork-core是Struts2框架的核心组件,它提供了Action和结果的执行模型,以及类型...总之,深入学习Struts-xwork-core的源码,将帮助你更好地掌握Struts2框架的精髓,提高开发效率,解决实际项目中遇到的问题。
本文将深入探讨XWork的源码,解析其设计理念和关键实现,帮助开发者更好地理解和使用Struts2。 1. **ActionInvocation**:XWork的核心是ActionInvocation,它是执行动作的实际载体。ActionInvocation维护了动作执行...
深入学习XWork源码,我们可以关注以下几个核心概念: - **Action接口与ActionSupport类**:Action接口定义了动作的基本行为,而ActionSupport是它的默认实现,提供了默认的错误处理和结果返回机制。 - **...
通过阅读源码,我们可以学习到如何设计可扩展的框架,以及如何利用拦截器实现AOP。此外,对于性能优化和问题排查,熟悉源码也具有极大的帮助。 总之,Struts2和XWork是Java Web开发中的重要工具,它们提供了强大的...
**Xwork源码详解** Xwork是Struts2框架的核心组成部分,主要负责处理Action的业务逻辑,它是表现层框架Struts2的内核。在深入理解Struts2的工作原理时,解析Xwork的源码至关重要。Xwork为Struts2提供了强大的命令...
这个源码包是理解Struts2工作原理的关键,因为它包含了xwork的核心实现,包括动作调度、依赖注入、类型转换、拦截器机制等多个关键模块。现在,让我们一起深入探讨xwork-2.0.4源码中的核心知识点。 1. **动作调度...
xwork 2.8的源码,最新版本欢迎下载!
通过对XWork源码的学习,开发者可以深入理解Struts2框架的内部运作机制,包括Action的生命周期、拦截器的执行顺序、OGNL的表达式解析等。这不仅有助于调试和优化代码,也能为自定义扩展或开发更复杂的web应用提供...
在XWork源码中,我们可以看到OGNL的解析和表达式求值过程,这对于理解Struts2中的数据绑定至关重要。 5. **TypeConverter**:XWork提供了一套强大的类型转换机制,使得不同类型的参数能够被自动转换成动作方法所需...