最近,一个曾经出现的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
问题解决,顺便附上源码包。
分享到:
相关推荐
Struts 2无须继承任何类型或实现任何接口,表单数据包含在Action中,通过Getter和Setter获取。 虽然,在理论上Struts2的Action无须实现任何接口或者是继承任何的类,但是,在实际编程过程中,为了更加方便的实现...
Apache官方已针对该漏洞发布安全公告,ApacheStruts 2.3.5 – 2.3.31版本及2.5 – 2.5.10版本存在远程代码执行漏洞(CNNVD-201703-152 ,CVE-2017-5638)。该漏洞是由于上传功能的异常处理函数没有正确处理用户输入...
2.Struts2和Struts1.x的差别, 最明显的就是Struts2是一个pull-MVC架构。 这是什么意思呢?从开发者角度看, ... Struts 2无须继承任何类型或实现任何接口,表单数据包含在Action中,通过Getter和Setter获取。
Struts2 是一个非常优秀的MVC框架,基于Model2 设计模型 由传统Struts1和WebWork两个经典框架发展而来 Struts2框架=Struts2+XWork Strust2 核心功能 允许POJO(Plain Old Java Objects,简单javabean对象,没有继承,...
用Struts2新建一个应用的方法步骤用Struts2新建一个应用的方法步骤: 1.首先新建一个Web Project。File——new——Web Project,在Project Name中输入一个合法的名字。例如:struts2 2. 单击采单Window——...
java 的eclipse或idea等 定义变量时 直接引入lombok.jar包 在类外部 引入@AllArgsConstructor @NoArgsConstructor @Data 简便方法引入getter/setter/tostring等方法
主要介绍了Vue getter setter,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
软件名称:排版助手 GIDOT TYPESETTER 当前版本:3.0.8 (2009-12-08 02:05:07) 作者:GIDOT 邮箱:GIDOT@VIP.QQ.COM 网站:WWW.GIDOT.NET/TYPESETTER 官方QQ群:56288291、13733967 新增功能: ·关闭时保存编辑现场...
排版助手gidot typesetter 是一款比较受欢迎的智能文章排版软件,这款排版软件不大,且不需要安装即可轻松实现编辑排版。 排版助手gidot typesetter主要功能:段落智能修正、去掉某些防拷贝的干扰符、修正段落首尾...
Laravel开发-setter 将数据库设置与Laravel集成的简单方法。
Typesetter CMS简介 TypesetterCMS是一个功能全面的网站内容管理系统,其嵌入式的管理接口可以让用户在一个简单的浏览器窗口中看到网站的改变。 Typesetter CMS特色功能: 真正的所见即所得编辑器 (CKEditor) ...
软件名称:排版助手 GIDOT TYPESETTER 当前版本:3.0.6 作者:GIDOT 邮箱:GIDOT@VIP.QQ.COM 网站:WWW.GIDOT.CN/TYPESETTER 新增功能: ·批量排版功能 ·多文本文件合并成单一文本文件 功能改进: ·将排版工具...
eclipse中的setter与getter注释,实体的编写,在代码编写工程中能清晰了解此意
Gidot TypeSetter文章自动排版工具
Java开发学习(六)----DI依赖注入之setter及构造器注入解析.doc
Gidot TypeSetter是一款广受编辑追捧的智能排版整理文章的软件,该软件短小精悍,无需安装即可使用。适合新闻采集员、编辑、文摘网及有文章排版需求者。 软件主要功能:段落智能修正、去掉某些防拷贝的干扰符、修正...
市面上的什么“深入浅出struts2”、“精通struts2”、“this struts2”、“that struts2”,读来让人感觉是在隔靴搔痒,戳不到struts2框架的本质之处,顶多可以称为参考手册。参考手册不适合通读、精读,因为读了...
IOC DI 一个spring4.2.0 spring setter 和构造 注入 的小例子
Typesetter cms是一个完整的内容管理系统,它可以用来创建丰富和可扩展性的站点,它的用户界面简单易用,内嵌的管理页面可以让用户在同一个窗口里即时的浏览站点页面的变更。
Typesetter CMS是一个功能全面的网站内容管理系统,其嵌入式的管理接口可以让用户在一个简单的浏览器窗口中看到网站的改变。 Typesetter CMS 5.1 更新日志:2017-08-12 页面编辑;部分可视性;剖面剪贴板;嵌入...