`

由一个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


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

相关推荐

    struts2 框架的核心jar包(struts-2.3.16版本)

    具体到struts-2.3.16版本,这是Struts2的一个稳定版本,包含了多个组件和修复的bug,旨在提供更高效、更安全的开发环境。 核心jar包是Struts2框架的基础,主要包括以下几个关键组件: 1. **struts2-core.jar**:这...

    struts2辅助jar包

    Struts2是一个流行的Java web应用程序框架,用于构建和维护可扩展、高效且易于维护的Web应用。在开发基于Struts2的应用时,除了主要的Struts2核心库外,还需要依赖一些辅助的JAR包来确保项目的正常运行。标题提到的...

    struts-json-plugin-2.1.8.jar struts2-junit-plugin-2.1.8.jar json-lib-2.1.jar

    Struts2是一个非常流行的Java Web开发框架,它提供了一种模型-视图-控制器(MVC)架构,便于开发者构建动态、数据驱动的Web应用程序。然而,在开发过程中,经常会出现库文件版本不兼容的问题,这可能导致程序运行...

    struts2的json插件

    Struts2是一个非常流行的Java Web框架,用于构建和维护可扩展且有组织的MVC(模型-视图-控制器)应用程序。JSON(JavaScript Object Notation)插件是Struts2框架的一个重要组成部分,它允许应用以JSON格式进行数据...

    struts 2.3.4.1 jar

    4. **Javaassist** (`javassist-3.11.0.GA.jar`): Javaassist是一个动态类转换和代码生成的库,Struts 2使用它来在运行时修改或创建Java类,实现如AOP(面向切面编程)等功能。 5. **Apache Commons Lang3** (`...

    struts2.3.1.1

    首先,Struts2是一个流行的开源框架,由Apache软件基金会维护。它的核心功能是处理HTTP请求,提供强大的动作调度,以及灵活的数据绑定和类型转换。在Struts2.3.1.1中,开发者可以期待一些特定的改进和新特性,例如...

    struts2 框架核心包

    Struts2框架是Java开发Web应用的一个流行选择,它基于MVC(Model-View-Controller)设计模式,提供了丰富的功能和强大的控制结构。这个“Struts2框架核心包”包含了Struts2运行所需的关键组件,使得开发者能够快速...

    struts2文件下载(解决了中文乱码问题)

    Struts2是一个非常流行的Java Web框架,用于构建MVC(模型-视图-控制器)架构的应用程序。在处理文件下载时,Struts2提供了一套完整的解决方案,包括处理中文文件名的乱码问题。在标题提到的场景中,开发者可能遇到...

    升级struts2.3.32所需jar包及操作说明

    Struts2是一个流行的Java web应用程序框架,用于构建和维护可扩展且易于管理的企业级应用。在标题和描述中提到的“升级struts2.3.32所需jar包及操作说明”是指对现有的Struts2应用程序进行版本更新,从一个较低的...

    struts2 jfreechart使用的jar包

    3. 将生成的图表转换为流(`InputStream`),然后在Struts2的结果配置中指定一个`StreamResult`,将图表的流发送到浏览器。 4. 在客户端,可以使用HTML和JavaScript(如jQuery或Ajax)来显示从服务器获取的图表。 ...

    struts2.2.1jar包

    - `javassist-3.7.ga.jar`:Javassist是一个用于操作和转换类字节码的库,Struts2利用它在运行时动态生成Action类,实现灵活的配置和插件机制。 6. **Commons IO**: - `commons-io-1.3.2.jar`:Apache Commons ...

    struts2+hibernate+spring

    Struts2是Struts1的升级版,它是一个基于MVC设计模式的Web应用框架,主要负责处理用户请求和展示结果。Struts2的核心是Action,它接收HTTP请求,执行业务逻辑,并通过Result返回相应的视图。此外,Struts2还提供了...

    ssh Struts2.3.16.1+Hibernate4.3.4+Spring4.0.2 框架整合jar包

    SSH框架是Java开发中常用的三大开源框架Struts2、Hibernate和Spring的组合,它们各自负责应用程序的不同层面,共同构建了一个高效、灵活的企业级应用开发环境。这个SSH整合jar包是针对Struts2.3.16.1、Hibernate...

    struts1.3.0

    Struts是Apache软件基金会下的一个开源项目,它是一款基于MVC(Model-View-Controller)设计模式的Java Web应用程序框架。Struts1.3.0是该框架的一个版本,主要适用于J2EE环境,用于构建高效、可扩展的Web应用。这个...

    struts_2.1.8.1相关jar包

    Struts 2是一个基于MVC(Model-View-Controller)设计模式的开源Java Web框架,由Apache软件基金会维护。在Web开发中,Struts 2提供了强大的功能,帮助开发者构建可扩展、易于维护的Web应用程序。本文将详细介绍...

    一个BBS系统,基于Struts2+MyBatis+Spring+MySQL开发,本人数据库课程设计_MrCBBS.zip

    首先,Struts2是Apache组织的一个开源项目,它是一个基于MVC设计模式的Web应用框架。在MVC模式中,Struts2扮演了控制器的角色,负责接收用户请求并调用相应的业务逻辑处理。在本系统中,Struts2作为控制层,协调用户...

    struts解决乱码问题

    - 由于Tomcat服务器的一个已知bug,GET请求参数默认被编码为ISO8859-1格式,即使设置了`request.setCharacterEncoding()`也无法更改。 - 处理方法: 1) 获取GET请求参数后,先将其转换为ISO8859-1格式的字节数组...

    statechart-1.0.zip

    Struts2 提供了一个灵活的 MVC 模式,支持拦截器、模板技术和多种结果类型,使得开发更高效、可测试性更强。它还与其他流行的 Java 框架如 Hibernate 和 Spring 集成良好。 2. **WebFlow**:WebFlow 是 ...

    SSH框架知识总结

    - **类型转换**: Struts2支持多种类型的请求参数转换,使得处理各种数据类型变得更加容易。 - **输入验证**: 可以针对特定的Action方法进行输入验证,提高了灵活性。 - **国际化支持**: 支持在全局、包或Action级别...

    struts2.0.11.rar_Java编程_Java_

    在Java编程领域,Struts2是一个重要的框架,它弥补了早期Struts1版本的一些不足,引入了许多增强功能和改进。Struts2的核心特性包括: 1. **拦截器(Interceptors)**:这是Struts2最强大的特性之一,拦截器可以...

Global site tag (gtag.js) - Google Analytics