`
lzy190100311
  • 浏览: 7622 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
社区版块
存档分类
最新评论

struts2请求转发之调用setter

阅读更多

最近,一个曾经出现的struts2报错 在项目部署在某服务器后再次出现,于是很有兴致的去挖了一下struts2的源代码(struts2-core-2.1.6、xwork-2.1.2、ognl-2.6.11)。报错如下:


ognl.MethodFailedException: Method "setEndtime" failed for object com.piptrade.action.tradetools.eCalerddarAction@17db177 [java.lang.NoSuchMethodException:
setEndtime([Ljava.lang.String;)]

首先需要的是开头,于是来到web.xml找到过滤器:

org.apache.struts2.dispatcher.FilterDispatcher

直接找到doFilter,关键源码(395行):

dispatcher.serviceAction(request, response, servletContext, mapping);

进入方法serviceAction,关键源码(468行):

proxy.execute();

进入类StrutsActionProxy方法execute,关键源码(52行):

return invocation.invoke();

之后在类DefaultActionInvocation方法invoke中进行一系列拦截器的调用(231-243行):

if (interceptors.hasNext()) {final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();String interceptorMsg = "interceptor: " + interceptor.getName();UtilTimerStack.push(interceptorMsg);try {resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);}finally {UtilTimerStack.pop(interceptorMsg);}}
 

而调用action中setter的拦截器名称是:

com.opensymphony.xwork2.interceptor.ParametersInterceptor

其中方法doIntercept中带有关键源码(186-187行):

ValueStack stack = ac.getValueStack();setParameters(action, stack, parameters);

继续进入方法setParameters,关键源码(248行、273行):

ValueStack newStack = valueStackFactory.createValueStack(stack);
newStack.setValue(name, value);

继续进入类OgnlValueStack方法setValue链,关键源码(155行):

ognlUtil.setValue(expr, context, root, value);

继续进入类OgnlUtil方法setValue,关键源码(192行):

Ognl.setValue(compile(name), context, root, value);

 

这时,方法调用到达ongl包的核心部分,关键源码(476行):

n.setValue( ognlContext, root, value );

调用链如下:
类ognl.SimpleNode(246行、177行):

evaluateSetValueBody(context, target, value);
setValueBody(context, target, value);

到达类ASTProperty方法setValueBody,关键源码(101行):

OgnlRuntime.setProperty( context, target, getProperty( context, target), value );

继续进入类OgnlRuntime方法setProperty,关键源码(1656行):

accessor.setProperty( context, target, name, value );

这时accessor类型为CompoundRootAccessor,
继续进入类CompoundRootAccessor方法setProperty,关键源码(49-52行):

if (OgnlRuntime.hasSetProperty(ognlContext, o, name)) {
OgnlRuntime.setProperty(ognlContext, o, name, value);
return;}

再次进入类OgnlRuntime方法setProperty,关键源码(1656行):

accessor.setProperty( context, target, name, value );

这时accessor类型为ObjectAccessor,
并调用到父类ObjectPropertyAccessor方法setProperty,关键源码(131行):

if (setPossibleProperty(context, target, name, value) == OgnlRuntime.NotFound)

继续,目标方法调用出现(75-76行):

 if (!OgnlRuntime.setMethodValue(ognlContext, target, name, value, true))
result = OgnlRuntime.setFieldValue(ognlContext, target, name, value) ? null : OgnlRuntime.NotFound;


这里涉及2个方法,顺便简单分析一下这2个方法的关键源码:
1、    OgnlRuntime.setMethodValue(964行):

callAppropriateMethod(context, target, target, m.getName(), propertyName, Collections.nCopies(1, m), args);

从中可见,ognl.MethodFailedException就是从这个callAppropriateMethod方法中抛出的
2、    OgnlRuntime.setFieldValue(1136行、1140行、1146行)

state = context.getMemberAccess().setup(context, target, f, propertyName);
f.set(target, value);
context.getMemberAccess().restore(context, target, f, propertyName, state);

从中可见,为field赋值时,如果访问范围是不可外部访问的,先改为可访问,赋值后再改为原访问范围

这2个方法都有一个共同的逻辑:
都判断了(1)传入参数的类型是否为setter方法的参数类型(isAssignableFrom)
否则(2)把传入参数转换为setter方法的参数类型(getConvertedType)
其中逻辑(2)使用了接口ognl.TypeConverter,xwork2中的对应实现类为

com.opensymphony.xwork2.ognl.OgnlTypeConverterWrapper

并且再提供了接口com.opensymphony.xwork2.conversion.TypeConverter,默认的实现类为

com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter


至此,到达本文开始提到的报错问题相关的源代码位置:
类XWorkBasicConverter方法convertValue,关键源码(102-103行):

else if (Date.class.isAssignableFrom(toType)) {
result = doConvertToDate(context, value, toType);}

方法doConvertToDate,关键源码(305行、310-319行、332-336行):

Locale locale = getLocale(context);
else if (java.sql.Timestamp.class == toType) {Date check = null;SimpleDateFormat dtfmt = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.MEDIUM,locale);SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT, locale);
SimpleDateFormat dfmt = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT,locale);
else if (java.util.Date.class == toType) {Date check = null;SimpleDateFormat d1 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale);SimpleDateFormat d2 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);SimpleDateFormat d3 = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);                

方法getLocale,源码(145-157行):

private Locale getLocale(Map<String, Object> context) {if (context == null) {return Locale.getDefault();}Locale locale = (Locale)context.get(ActionContext.LOCALE);if (locale == null) {locale = Locale.getDefault();}return locale;}


分析:由于java.sql.Timestamp extends java.util.Date,
Date.class.isAssignableFrom判断成功,进入doConvertToDate,

其中使用的Locale变量取自getLocale方法,当context中未被设置Locale时,使用的是本地的默认值。

因此,不管是Date还是Timestamp,当服务器的默认Locale不是通常使用的zh_CN时,转换异常,返回后在callAppropriateMethod中抛出java.lang.NoSuchMethodException。

解决方法有两种:
1、    服务器配置:将服务器的环境变量Lang改为zh_CN.UTF-8
2、    工程配置:对配置文件struts.properties,
写入struts.locale=zh_CN,顺便加上struts.i18n.encoding=UTF-8

 

注:方法2在struts2.1.6下发现无效,经查是由于xwork-core-2.1.2中如下代码段缺少造成的:

ParametersInterceptor.setParameters

缺少代码

//keep locale from original context    
context.put(ActionContext.LOCALE,stack.getContext().get(ActionContext.LOCALE));
 

建议替换为已解决此bug的struts2.1.8

 

或者将新版本的ParametersInterceptor类编译覆盖源文件,地址:

http://svn.apache.org/viewvc?view=revision&revision=956389

问题解决,顺便附上源码包。

分享到:
评论

相关推荐

    Struts2 处理多个请求

    动态方法调用是Struts2提供的一种机制,允许用户在表单提交时指定一个Action实例下的特定方法来处理请求。在HTML表单中,`action`属性不再是直接指向Action类的名字,而是以`ActionName!methodName.action`的形式...

    struts2 学习重点笔记

    - **原理**:通过拦截器实现,拦截器会查找与 Action 类中的 setter 方法相匹配的请求参数,并调用这些方法填充数据。 **3.3 数据传递** - **方法**:通过 Action 的 getter 方法获取数据,然后将其添加到 request ...

    马士兵Struts2笔记2013

    在Struts2中,你可以创建领域对象,通过setter和getter方法来接收和设置请求参数,这些对象可以在多个Action之间共享,提高了代码的复用性。 4. **Struts2_2.1.6版本的中文问题** 在某些版本的Struts2中,可能出现...

    struts2简单实例

    - 请求参数绑定:Struts2支持自动将HTTP请求参数绑定到Action的属性上,例如,如果URL参数中有`id`,那么`setStudentId(int id)`会被调用来设置Action中的`studentId`属性。 - 结果转发:当Action执行完毕后,会...

    Struts2--为Action的属性注入值

    当用户发送一个HTTP请求,比如`http://example.com/myAction?username=John&age=30`,Struts2的拦截器会自动调用setter方法将请求参数`username`和`age`的值注入到Action的相应属性中。这是通过`...

    struts2用户管理

    当用户提交表单时,Struts2会根据配置的ActionMapping将请求转发到相应的execute()方法。在该方法中,我们获取表单参数,调用UserDAO的insert()方法,将数据保存到数据库。 **3. 删除用户** 删除用户也是通过...

    struts2类型转换和国际化

    3. 在调用setter方法前,Struts2会检查该属性是否有自定义的类型转换器。如果存在,它会在Action同级目录下的`action名称-conversion.properties`文件中查找转换规则。若在`src`目录下的`-conversion.properties`...

    struts2 接收参数

    7. **Struts2的类型转换**:Struts2提供了内置的类型转换器,可以自动将请求参数转换为Java对象。如果需要自定义转换逻辑,可以实现`Converter`接口。 在`Struts2_0900_ModelDrivenParamInput`这个文件中,可能包含...

    struts2 实现oracle数据库的增删改查

    Struts2是一个强大的MVC(模型-视图-控制器)框架,它被广泛应用于Java Web开发中,提供了灵活的架构和丰富的插件支持。在本教程中,我们将深入探讨如何利用Struts2来实现对Oracle数据库的CRUD(创建、读取、更新和...

    struts1.x 和 struts2.x向Action里填充jsp参数原理

    3. Struts自动将请求参数值绑定到ActionForm的属性上,这得益于JavaBean规范中的getter和setter方法。 4. ActionServlet调用ActionForm的validate()方法进行表单验证。 5. 如果验证成功,ActionServlet将ActionForm...

    struts2基本知识

    ### Struts2基础知识点...从过滤器和拦截器的作用到动态方法调用的实现,再到具体的登录和注册请求处理示例,旨在帮助读者全面了解Struts2的基本概念和使用方法。希望本文能对你在学习Struts2框架的过程中有所帮助。

    struts2框架学习笔记整理

    - **请求处理流程**:当用户请求到达时,Struts2的前端控制器会根据配置将请求交给相应的Action处理。 #### 二、Struts2框架搭建与配置 ##### 2.1 导入Jar包 - **下载Struts2的Jar包**:首先需要下载Struts2的库...

    struts1&struts2

    - **Struts2** 为每个请求创建新的Action实例,解决了线程安全问题,同时也更易于管理和测试。 5. **Servlet依赖** - **Struts1.x** 的Action类直接依赖于Servlet API,这使得测试变得更加困难。 - **Struts2** ...

    STRUTS2+HIBERNATE详细的分页实现代码详细的分页实现代码

    为了实现分页功能,我们需要在Struts2中定义Action类来接收用户的翻页请求,并通过Hibernate进行数据查询。具体步骤如下: 1. **定义PageTool类** 在Struts2中,我们通常会定义一个PageTool类来封装分页所需的信息...

    Struts2_OGNL

    OGNL(Object-Graph Navigation Language)是Struts2框架中的一个重要组件,用于在Web应用中访问和操作Java对象的属性,调用其方法,以及执行类型转换等操作。 OGNL是对象图导航语言的缩写,它是一种功能强大的...

    struts2实战案例(项目+建库语句)

    - **MVC模式**:理解模型、视图和控制器之间的关系,如何将用户请求转发给Action,Action如何调用模型(业务逻辑)并返回结果给视图(如JSP页面)。 - **Struts2配置**:熟悉`struts.xml`配置文件,包括Action的...

    应用Struts2处理表单数据

    4. **拦截器(Interceptors)**:Struts2的核心特性之一是拦截器,它们在Action调用前后执行,可以用来处理验证、日志、事务管理等任务。例如,`params`拦截器负责将请求参数绑定到Action,`validation`拦截器处理...

    struts2-crud.zip_DEMO_Java web CRUD demo_struts2 CRUD_struts2 de

    3. **Interceptor(拦截器)**:拦截器是Struts2的核心特性之一,它们按照预定义的顺序在Action调用前后执行,可以实现事务管理、权限验证等功能。 4. **Result类型**:Action执行后,会返回一个Result,决定如何...

    struts2学习笔记

    当客户端提交表单时,Struts2会自动调用setter方法将表单数据注入到Action中。例如,可以通过`ActionContext.getContext().put("newsList", newsList)`将新闻列表存入上下文,然后在页面上通过`”newsList”&gt;`标签...

Global site tag (gtag.js) - Google Analytics