`

由一个bug观Struts2的类型转换器

阅读更多

 

Bug.:

 

  /-- Encapsulated exception ------------\

java.lang.NoSuchMethodException: setYycbSq([Ljava.lang.String;)

     at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:810)

     at ognl.OgnlRuntime.setMethodValue(OgnlRuntime.java:964)

     at ognl.ObjectPropertyAccessor.setPossibleProperty

(ObjectPropertyAccessor.java:75)

     at ognl.ObjectPropertyAccessor.setProperty (ObjectPropertyAccessor.java:

131)

     at com.opensymphony.xwork2.ognl.accessor.ObjectAccessor.setProperty

(ObjectAccessor.java:28)

     at ognl.OgnlRuntime.setProperty(OgnlRuntime.java:1656)

     at ognl.ASTProperty.setValueBody(ASTProperty.java:101)

     at ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:177)

 

 

 

在使用 Struts2 框架开发时,有时会遇到上面的异常, Action 类里定义的实体类对象的属性或者直接在 action 中定义的属性变量中如有 Double 类型或 Float 类型时,当页面传值为 0 0.0 0.00 等为 0 的值时,有时很可能报上面的异常,原因是用户请求后进入到相应的 Action 中,首先执行的是封装页面获取的属性值,即调用属性的 get(),set() 方法,此时由于 struts2 核心包的版本低或者 jar 包冲突覆盖部分功能等问题,不能处理将从页面获取的“ 0 ”或“ 0.0 ”,“ 0.00 转换成 Double 类型或 Float 类型(但可以转换成 Integer 类型),故在通过调用 set() 方法封装页面属性值时就会报类型转换的异常。

 

     Struts2 的类型转换器虽然实现了基本类型转换功能,可以将 String 类型转换为 Integer Double Float Date 等类型,但是对于“ 0 ”或“ 0.0 ”,这个特殊的字符串来说,有时还避免不了会报类型转换的错误。从上面的异常信息我们可以看出,其实 Struts2 是利用 OGNL 进行类型转换的。

 

 使用 Struts2 框架,在页面传 0 ,或 0.0 时,这个异常也不是一定会出现的,有时OGNL也可以帮你正常转换成功,但如果因为 jar 包或其他问题而出现时这样的 bug 时,你可以考虑去下载最新版本的 jar 包;或者在页面传值时加以限定,比如为 Double 值时要求所传值大于 0 等;也可以在 action 里定义变量时尽量用 String 类型代替 Double Float 类型(这样就没利用 Struts2 类型自动转换这个优点了)。

 

    下面是我在网上找到的一点关于对 Struts2 的默认转换的阐述,觉得挺详细的,有利于我们了解 Struts2 类型转换器实现原理

 

 

       Struts2 可以自动完成大多数常用的类型转换,目前已支持的与字符串之间的转换类型包括:

l        String

l        boolean/Boolean

l        char/Character

l        int/Integer,float/Float,long/Long,double/Double

l        dates - 使用当前 request 指定的 Locale 信息对应的 SHORT 格式

l        arrays - 假定每一个字符串都能够转换成对应的数组元素

l        collections - 如果不能确定对象类型 , 将假定集合元素类型为 String, 并创建一个新的 ArrayList

注意,对于数组的类型转换将按造数组元素的类型来单独转换每一个元素。而在其他类型转换中,如果转换无法实现 , 将使用标准的类型转换错误报告。


      Struts2 几乎支持了所有的类型,如果没有什么特别的需求,完全没有必要自己去配置类型转换,这也可以说是 Struts2 一个比较优秀的方面。

 

转换器的源码分析:


     现在通过源代码来分析一下 Struts2 是如何实现这些转换器功能的。想弄清楚类型转换是怎么实现的,首先必须找到源代码的起点。如果需要自己手动配置一个类型转换器的时候,可以通过继承 org.apache.struts2.util.StrutsTypeConverter 来实现,在这个类中提供了两个相互转换的函数,通过这两个函数可以很方便的编写处理其他对象和字符串相互转换的类型转换器。这个类的源代码文件的完整路径是: \struts-2.0.9-all\struts-2.0.9\src\core\src\main\java\org\apache\struts2\util 。那么就先看看这个类的代码片断:

 

StrutsTypeConverter.java

 

package org.apache.struts2.util;

import java.util.Map;

import ognl.DefaultTypeConverter;

public abstract class Struts2TypeConverter extends DefaultTypeConverter {

    public Object convertValue(Map context, Object o, Class toClass) {

        //根据不同的类型分别调用不同的转换函数


        if (toClass.equals(String.class)) {

            //其它类型向字符串类型的转换


            return convertToString(context, o);

        } else if (o instanceof String[]) {

            //字符串类型向其他类型转换


            return convertFromString(context, (String[]) o, toClass);

        } else if (o instanceof String) {

            return convertFromString(context, new String[]{(String) o}, toClass);

        } else {

           return performFallbackConversion(context, o, toClass);

        }

    } 

    protected Object performFallbackConversion(Map context, Object o, Class toClass) {

    return super.convertValue(context, o, toClass);

    } 

    public abstract Object convertFromString(Map context, String[] values, Class toClass);

    public abstract String convertToString(Map context, Object o);

}
 

     在这个类中定义了 convertValue() 方法,该方法就是为了完成字符串与其他类型之间的转换。如果是其他类型向字符串转换,即 toClass.equals(String.class) ,那么就会调用 convertToString(context, o) 方法,这个方法在下面已经定义了,是一个抽象方法,在子类当中必须加以实现,不同的类型转换为 String 类型有不同的方法。同理,如果是字符串向其他类型的转换就会调用 convertFromString(context, (String[]) o, toClass) 方法,这也是一个抽象方法,需要在子类中自己实现。所以如果想自己实现类型转换器,就只要继承这个类,然后实现类中这两个方法就可以了。如果没有类型要转换,就会执行最后一个 else 语句,就会做到 return performFallbackConversion(context, o, toClass) ,这个方法就只是调用父类中 convertValue(context, o, toClass) ,就是说如果默认的话,就会执行父类中 convertValue(context, o, toClass) 方法。

我们的目的是为了看到 Struts2 在什么地方有对数据类型转换的相关代码,那么就要再向上看这个类的父类 ognl.DefaultTypeConverter 类。从包名可以知道这是在 ognl 包中的一个类。到 ognl 源代码中搜索这个类可以很快找到这个类的源代码。


注意:在 Struts2 压缩包是没有 ognl 的源代码的,需要读者自己再到 ognl 官网去下一个源代码的压缩包,随便选择一版本下载,这一部分代码实现都大同小异,当然最好使用版本相符的版本。


下面就进入 DefaultTypeConverter 类看看它是如何实现的。


DefaultTypeConverter.java

 

package ognl;

import java.lang.reflect.Member;

import java.util.Map;

public class DefaultTypeConverter implements TypeConverter{

    //构造函数

    public DefaultTypeConverter(){

        super();

    }

    //实现类型转换的函数
    public Object convertValue(Map context, Object value, Class toType){

        return OgnlOps.convertValue(value, toType);

    }

    public Object convertValue(Map context, Object target, Member member, 
String propertyName, Object value, Class toType){

        return convertValue(context, value, toType);

    }

}
 

      StrutsTypeConverter 类从 DefaultTypeConverter 类继承了 convertValue() 方法,在这个父类中的 convertValue() 方法还是继续返回类 OgnlOps 中静态的 convertValue(value, toType) ,这样就表明默认的类型转换是在类 OgnlOps convertValue(value, toType) 中实现的。是不是这样呢?我们先看看这个实现的接口 TypeConverter 的源代码是怎么样的。

 

 

TypeConverter.java

 
package ognl;

import java.lang.reflect.Member;

import java.util.Map;

public interface TypeConverter{

    public Object convertValue(Map context, Object target, Member member, 
String propertyName, Object value, Class toType);

} 
 

   这个接口中就只是定义了一个convertValue()方法,所以最后的实现应该就是在类OgnlOps的convertValue(value, toType)中完成的。我们先来看看这个类的源代码。

OgnlOps.java

 

package ognl;

/*省略导入的包*/

public abstract class OgnlOps implements NumericTypes{

   /*省略其他的方法函数*/

    public static Object convertValue( Object value, Class toType){

        Object  result = null;

        if (value != null) {

            //不同类型的具体转换

            if ( value.getClass().isArray() && toType.isArray() ) {

                Class       componentType = toType.getComponentType();

                result = Array.newInstance(componentType, Array.getLength(value));

                for (int i = 0, icount = Array.getLength(value); i < icount; i++) {

                    Array.set(result, i, convertValue(Array.get(value, i), componentType));

                }

            } else {

                if ( ( toType == Integer.class ) || ( toType == Integer.TYPE ) )      
                                               result = new Integer((int)longValue(value));

                if ( ( toType == Double.class ) || ( toType == Double.TYPE ) )          
                                               result = new Double(doubleValue(value));

                if ( ( toType == Boolean.class ) || ( toType == Boolean.TYPE ) )       
                                               result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE;

                if ( ( toType == Byte.class ) || ( toType == Byte.TYPE ) )            
                                               result = new Byte((byte)longValue(value));

                if ( ( toType == Character.class ) || ( toType == Character.TYPE ) )   
                                              result = new Character((char)longValue(value));

                if ( ( toType == Short.class ) || ( toType == Short.TYPE ) )           

                                               result = new Short((short)longValue(value));

                if ( ( toType == Long.class ) || ( toType == Long.TYPE ) )             

                                               result = new Long(longValue(value));

                if ( ( toType == Float.class ) || ( toType == Float.TYPE ) )           

                                               result = new Float(doubleValue(value));

                if ( toType == BigInteger.class )                                      

                                               result = bigIntValue(value);

                if ( toType == BigDecimal.class )                                      

                                               result = bigDecValue(value);

                if ( toType == String.class )                                          

                                               result = stringValue(value);

            }

        } else {

            if (toType.isPrimitive()) {

                result = OgnlRuntime.getPrimitiveDefaultValue


(toType);

            }

        }

        return result;

    }

}

      

       看到这个类的源代码,我们只对 convertValue( Object value, Class toType) 函数感兴趣,可以看到它是一个静态方法,正是这个函数完成许多基本的类型转换。从上面代码中应该很清楚 Struts2 默认的类型转换是怎么实现的了。代码中粗体的部分就是对应的可以被转化的类型,用了多个 if/else 语句,完成了常用类型的转换。每种类型的转换方法都不一样,具体转换代码如源代码所示。这个类当中还有许多其他函数,在此省略了。

通过查找和分析我们发现,这个类型转换的操作早就在 ognl 中实现, Struts2 只是引用了这个包中的函数,然后在自己的源码中进行了一些封装,以便使用的时候可以方便的访问到,不用再去访问 ognl 中的函数。当然 Struts2 早就把 ognl 包作为必须装载的 jar 包之一。

 

  


By the way, 供大家交流Pentaho的圈子,里面可以共享有关pentahoBI平台学习的资料,期待您的加入! http://pentahofrends.group.iteye.com/group/share

 

 

2
2
分享到:
评论
2 楼 ruinxdgzy 2010-11-07  
回复1楼:
    测试过了,只要是大于0的double类型数值都不报错,唯独输入0或 0.0就报错,后来没办法换了最新版本的jar包才解决。而在另一个项目中jar包版本虽低但却不报错。很是郁闷呢。
1 楼 kimmking 2010-11-07  
  /-- Encapsulated exception ------------\

java.lang.NoSuchMethodException: setYycbSq


看起来不是转换问题,而是有地方写错了吧。

相关推荐

    Bug管理系统 struts2+sping2.5+hibernate3(2-2)

    本程序是作者学习struts spring hibernate构架后为了练习开发的一个小程序。开发此程序的目的是为了验证框架技术在项目中的应用。本程序尽量包含了开发当中遇到的一些问题及解决方案。同时欢迎广大网友到作者的群内...

    Struts2 Struts2 超好的Struts2 pdf 文档

    Struts2 Struts2 超好的Struts2 pdf 文档 Struts2 Struts2 超好的Struts2 pdf 文档 Struts2.pdf文档

    struts2_S016_S017_repair

    官方描述: ... ... 官方建议修复方案:升级到最新版本 struts-2.3.15.1 但通常现有系统升级,可能导致不稳定及与其他...鉴于此csdn网友jzshmyt整理了一种既可以不用升级现有struts版本,有能完美解决这两个漏洞的方案,

    Struts2权威指南完整版

    相对于2007年发布的Struts 2.0,Struts 2.1改变较大,Struts 2.1不仅修正了Struts 2.0中少量Bug(这些Bug在《Struts 2.1权威指南》第一版中已经指出)。而且新增了REST、Convention和Java Templates,这些都是Struts 2...

    struts2-json-plugin-2.3.15.1 -Lee修复bug版.jar

    struts2-json-plugin-2.3.15.1 -Lee修复bug版.jarstruts2-json-plugin-2.3.15.1 -Lee修复bug版.jarstruts2-json-plugin-2.3.15.1 -Lee修复bug版.jarstruts2-json-plugin-2.3.15.1 -Lee修复bug版.jar

    struts2 cve-2016-3081 BUG版本升级整理

    cve-2016-3081 BUG版本升级整理,里面有Struts 2.1.8 升级 Struts 2.3.28需要替换的jar包,如不是2.1.8的可以做一个简单的参考。可能每个人中引用的jar包不同,但通过参考能节省调试时间,希望对大家有帮助

    struts2官方中文帮助文档最新修复Bug版(绝版)

    struts2官方中文帮助文档最新修复Bug版(绝版),相信不用我多说大家都知道是干什么的,开发者都知道,辛苦整理的这个资源,希望大家喜欢,希望对大家有帮助,谢谢大家,请给个五星好评,谢谢啦!

    Struts2+hibernate+spring的常见面试题

    Apache Struts2的是一个在Java中构建Web应用程序开源框架。 Struts2是基于OpenSymphony的WebWork的框架。它是Struts1的提高,它更加灵活,易于使用和扩展。 Struts2的核心组成部分是Action,拦截器和结果页。 ...

    struts2上传必备jar包,避免出现struts2的升级漏洞!自己吃亏后分享

    这个bug是由Struts2上传文件后return SUCCESS后报的错误: java.lang.AbstractMethodError: be.telio.mediastore.ui.upload.GarryMultiPartRequest.cleanUp()V at org.apache.struts2.dispatcher.multipart....

    Struts2+hibernate3

    Struts2+hibernate3+mysql+myseclipse 开发项目 常见bug

    struts2漏洞解决

    解决Struts2存在的重大后门BUG,并附带详细介绍文档

    Struts2学习示例

    struts2 示例 目的:构建struts2开发框架 适合:有一定struts2基础 实现:通过对用户信息的基本操作来给大家做个示范 1、struts2的搭建 2、web.xml文件的简单配置 3、struts.xml文件的简单配置 4、网站初始化工作...

    迅捷pdf转换成word转换器 v6.3.zip

    迅捷pdf转换成word转换器是一款专业PDF转Word软件。软件功能强大、操作简单,用户只需把PDF文件拖拽到软件界面中,然后单击“转换”即可完成转换。使用本软件,可以很方便快捷地把pdf文档转换为word文档、txt文本、...

    struts2-016/017漏洞解决

    在不升级原框架的基础上解决漏洞

    XSS转码 && struts2 property标签的bug

    NULL 博文链接:https://sw1982.iteye.com/blog/975340

    Bug管理系统 struts2+sping2.5+hibernate3(1-2)

    本程序是作者学习struts spring hibernate构架后为了练习开发的一个小程序。开发此程序的目的是为了验证框架技术在项目中的应用。本程序尽量包含了开发当中遇到的一些问题及解决方案。同时欢迎广大网友到作者的群内...

    struts2+kinEditor整合

    2、复制struts需要的jar包和kindEditor必须jar包到lib下,因为它们的jar包邮重复,选择最新的就ok 3、如果需要后台java代码获取内容,可以在action中定义content属性,getContent就可以获取到 需要注意的是,这里的 ...

    ssb(struts2,spring3,mybatis3)整合实现的家庭费用记录系统

    ssb(struts2,spring3,mybatis3)实现的家庭费用记录系统,页面已经做好,思路是根据本人实际情况定制:总支出与总收入不计算信用卡、每月记录消费时需先记录本月原有金额等。 功能包括:本月当天支出于收入、本月总支...

Global site tag (gtag.js) - Google Analytics