`
jdluojing
  • 浏览: 16408 次
  • 性别: Icon_minigender_1
文章分类
社区版块
存档分类
最新评论

Struts2学习笔记(十一) 类型转换(Type Conversion)(上)

 
阅读更多

类型转换概述

把请求参数映射到动作属性的工作是由Parameters拦截器来负责,它是defaultStack拦截器栈中的医院。我们知道,所有的请求参数都是String类型,但是动作的属性却并不都是String类型,那么肯定需要通过某种方式来实现String类型和其他数据类型之间的转换。前面我刚刚学习了OGNL,我们知道通过OGNL能够在拦截器和视图中操作我们的Action成员属性,我们也知道将请求参数映射到Action属性的工作是由Parameters拦截器来实现,那么我们可以推测Struts2正是通过这二者的结合来完成数据类型的转换的。如果我们查看Struts2的源代码,我们就会发现Strtus2确实是通过OGNL的API来实现类型转换的。

OGNL中有一个TypeConversion接口,实现这个类接口的类都可以被当作类型转换器,并且OGNL提供了一个默认的实现类DefaultTypeConversion,这个类通过调用OgnlOps类的converteValue静态方法来实现类型转换:

public static Object convertValue(Object value, Class toType, boolean preventNulls)
    {
        Object result = null;
        
        if (value != null && toType.isAssignableFrom(value.getClass()))
            return value;
        
        if (value != null) {
            /* If array -> array then convert components of array individually */
            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 (value.getClass().isArray() && !toType.isArray()) {
                
                return convertValue(Array.get(value, 0), toType);
            } else if (!value.getClass().isArray() && toType.isArray()){
                
                if (toType.getComponentType() == Character.TYPE) {

                    result = stringValue(value).toCharArray();
                } else if (toType.getComponentType() == Object.class) {
                    return new Object[] { value };
                }
            } 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);
            } else if (preventNulls && toType == Boolean.class) {
                result = Boolean.FALSE;
            } else if (preventNulls && Number.class.isAssignableFrom(toType)){
                result = OgnlRuntime.getNumericDefaultValue(toType);
            }
        }
        
        if (result == null && preventNulls)
            return value;

        if (value != null && result == null) {
            
            throw new IllegalArgumentException("Unable to convert type " + value.getClass().getName() + " of " + value + " to type of " + toType.getName());
        }

        return result;
}

Struts2中也有一个DefaultTypeConverter,该类实现了ognl.TypeConverter接口,并且实现了一些常用数据类型的转换。XWorkConverter类继承了DefaultTypconvertor类,并在其中做了一些扩展,正式这些扩展实现了Strtus2特色的自定义类型转换功能。在XworkConverter类中重写了DefaultTypeConverter类的convertValue方法。(具体的细节可以查看XworkConverter类的源码)

public Object convertValue(Map<String, Object> context, Object target, Member member, String property, Object value, Class toClass) {
        //
        // Process the conversion using the default mappings, if one exists
        //
        TypeConverter tc = null;

        if ((value != null) && (toClass == value.getClass())) {
            return value;
        }

        // allow this method to be called without any context
        // i.e. it can be called with as little as "Object value" and "Class toClass"
        if (target != null) {
            Class clazz = target.getClass();

            Object[] classProp = null;

            // this is to handle weird issues with setValue with a different type
            if ((target instanceof CompoundRoot) && (context != null)) {
                classProp = getClassProperty(context);
            }

            if (classProp != null) {
                clazz = (Class) classProp[0];
                property = (String) classProp[1];
            }

            tc = (TypeConverter) getConverter(clazz, property);

            if (LOG.isDebugEnabled())
                LOG.debug("field-level type converter for property [" + property + "] = " + (tc == null ? "none found" : tc));
        }

        if (tc == null && context != null) {
            // ok, let's see if we can look it up by path as requested in XW-297
            Object lastPropertyPath = context.get(ReflectionContextState.CURRENT_PROPERTY_PATH);
            Class clazz = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
            if (lastPropertyPath != null && clazz != null) {
                String path = lastPropertyPath + "." + property;
                tc = (TypeConverter) getConverter(clazz, path);
            }
        }

        if (tc == null) {
            if (toClass.equals(String.class) && (value != null) && !(value.getClass().equals(String.class) || value.getClass().equals(String[].class))) {
                // when converting to a string, use the source target's class's converter
                tc = lookup(value.getClass());
            } else {
                // when converting from a string, use the toClass's converter
                tc = lookup(toClass);
            }

            if (LOG.isDebugEnabled())
                LOG.debug("global-level type converter for property [" + property + "] = " + (tc == null ? "none found" : tc));
        }


        if (tc != null) {
            try {
                return tc.convertValue(context, target, member, property, value, toClass);
            } catch (Exception e) {
                if (LOG.isDebugEnabled())
                    LOG.debug("unable to convert value using type converter [#0]", e, tc.getClass().getName());
                handleConversionException(context, property, value, target);

                return TypeConverter.NO_CONVERSION_POSSIBLE;
            }
        }

        if (defaultTypeConverter != null) {
            try {
                if (LOG.isDebugEnabled())
                    LOG.debug("falling back to default type converter [" + defaultTypeConverter + "]");
                return defaultTypeConverter.convertValue(context, target, member, property, value, toClass);
            } catch (Exception e) {
                if (LOG.isDebugEnabled())
                    LOG.debug("unable to convert value using type converter [#0]", e, defaultTypeConverter.getClass().getName());
                handleConversionException(context, property, value, target);

                return TypeConverter.NO_CONVERSION_POSSIBLE;
            }
        } else {
            try {
                if (LOG.isDebugEnabled())
                    LOG.debug("falling back to Ognl's default type conversion");
                return super.convertValue(value, toClass);
            } catch (Exception e) {
                if (LOG.isDebugEnabled())
                    LOG.debug("unable to convert value using type converter [#0]", e, super.getClass().getName());
                handleConversionException(context, property, value, target);

                return TypeConverter.NO_CONVERSION_POSSIBLE;
            }
        }
}
protected void addConverterMapping(Map<String, Object> mapping, Class clazz) {
        try {
            String converterFilename = buildConverterFilename(clazz);
            InputStream is = FileManager.loadFile(converterFilename, clazz);

            if (is != null) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("processing conversion file [" + converterFilename + "] [class=" + clazz + "]");
                }

                Properties prop = new Properties();
                prop.load(is);

                for (Map.Entry<Object, Object> entry : prop.entrySet()) {
                    String key = (String) entry.getKey();

                    if (mapping.containsKey(key)) {
                        break;
                    }
                    // for keyProperty of Set
                    if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX)
                            || key.startsWith(DefaultObjectTypeDeterminer.CREATE_IF_NULL_PREFIX)) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as String]");
                        }
                        mapping.put(key, entry.getValue());
                    }
                    //for properties of classes
                    else if (!(key.startsWith(DefaultObjectTypeDeterminer.ELEMENT_PREFIX) ||
                            key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX) ||
                            key.startsWith(DefaultObjectTypeDeterminer.DEPRECATED_ELEMENT_PREFIX))
                            ) {
                        TypeConverter _typeConverter = createTypeConverter((String) entry.getValue());
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as TypeConverter " + _typeConverter + "]");
                        }
                        mapping.put(key, _typeConverter);
                    }
                    //for keys of Maps
                    else if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX)) {

                        Class converterClass = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());

                        //check if the converter is a type converter if it is one
                        //then just put it in the map as is. Otherwise
                        //put a value in for the type converter of the class
                        if (converterClass.isAssignableFrom(TypeConverter.class)) {
                            TypeConverter _typeConverter = createTypeConverter((String) entry.getValue());
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as TypeConverter " + _typeConverter + "]");
                            }
                            mapping.put(key, _typeConverter);
                        } else {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as Class " + converterClass + "]");
                            }
                            mapping.put(key, converterClass);
                        }
                    }
                    //elements(values) of maps / lists
                    else {
                        Class _c = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as Class " + _c + "]");
                        }
                        mapping.put(key, _c);
                    }
                }
            }
        } catch (Exception ex) {
            LOG.error("Problem loading properties for " + clazz.getName(), ex);
        }

        // Process annotations
        Annotation[] annotations = clazz.getAnnotations();

        for (Annotation annotation : annotations) {
            if (annotation instanceof Conversion) {
                Conversion conversion = (Conversion) annotation;

                for (TypeConversion tc : conversion.conversions()) {

                    String key = tc.key();

                    if (mapping.containsKey(key)) {
                        break;
                    }
                    if (LOG.isDebugEnabled()) {
                        LOG.debug(key + ":" + key);
                    }

                    if (key != null) {
                        try {
                            if (tc.type() == ConversionType.APPLICATION) {
                                defaultMappings.put(key, createTypeConverter(tc.converter()));
                            } else {
                                if (tc.rule().toString().equals(ConversionRule.KEY_PROPERTY) || tc.rule().toString().equals(ConversionRule.CREATE_IF_NULL)) {
                                    mapping.put(key, tc.value());
                                }
                                //for properties of classes
                                else if (!(tc.rule().toString().equals(ConversionRule.ELEMENT.toString())) ||
                                        tc.rule().toString().equals(ConversionRule.KEY.toString()) ||
                                        tc.rule().toString().equals(ConversionRule.COLLECTION.toString())
                                        ) {
                                    mapping.put(key, createTypeConverter(tc.converter()));


                                }
                                //for keys of Maps
                                else if (tc.rule().toString().equals(ConversionRule.KEY.toString())) {
                                    Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
                                    if (LOG.isDebugEnabled()) {
                                        LOG.debug("Converter class: " + converterClass);
                                    }
                                    //check if the converter is a type converter if it is one
                                    //then just put it in the map as is. Otherwise
                                    //put a value in for the type converter of the class
                                    if (converterClass.isAssignableFrom(TypeConverter.class)) {
                                        mapping.put(key, createTypeConverter(tc.converter()));
                                    } else {
                                        mapping.put(key, converterClass);
                                        if (LOG.isDebugEnabled()) {
                                            LOG.debug("Object placed in mapping for key "
                                                    + key
                                                    + " is "
                                                    + mapping.get(key));
                                        }

                                    }

                                }
                                //elements(values) of maps / lists
                                else {
                                    mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
                                }
                            }
                        } catch (Exception e) {
                        }
                    }
                }
            }
        }

        Method[] methods = clazz.getMethods();

        for (Method method : methods) {

            annotations = method.getAnnotations();

            for (Annotation annotation : annotations) {
                if (annotation instanceof TypeConversion) {
                    TypeConversion tc = (TypeConversion) annotation;

                    String key = tc.key();
                    if (mapping.containsKey(key)) {
                        break;
                    }
                    // Default to the property name
                    if (key != null && key.length() == 0) {
                        key = AnnotationUtils.resolvePropertyName(method);
                        LOG.debug("key from method name... " + key + " - " + method.getName());
                    }


                    if (LOG.isDebugEnabled()) {
                        LOG.debug(key + ":" + key);
                    }

                    if (key != null) {
                        try {
                            if (tc.type() == ConversionType.APPLICATION) {
                                defaultMappings.put(key, createTypeConverter(tc.converter()));
                            } else {
                                if (tc.rule().toString().equals(ConversionRule.KEY_PROPERTY)) {
                                    mapping.put(key, tc.value());
                                }
                                //for properties of classes
                                else if (!(tc.rule().toString().equals(ConversionRule.ELEMENT.toString())) ||
                                        tc.rule().toString().equals(ConversionRule.KEY.toString()) ||
                                        tc.rule().toString().equals(ConversionRule.COLLECTION.toString())
                                        ) {
                                    mapping.put(key, createTypeConverter(tc.converter()));
                                }
                                //for keys of Maps
                                else if (tc.rule().toString().equals(ConversionRule.KEY.toString())) {
                                    Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
                                    if (LOG.isDebugEnabled()) {
                                        LOG.debug("Converter class: " + converterClass);
                                    }
                                    //check if the converter is a type converter if it is one
                                    //then just put it in the map as is. Otherwise
                                    //put a value in for the type converter of the class
                                    if (converterClass.isAssignableFrom(TypeConverter.class)) {
                                        mapping.put(key, createTypeConverter(tc.converter()));
                                    } else {
                                        mapping.put(key, converterClass);
                                        if (LOG.isDebugEnabled()) {
                                            LOG.debug("Object placed in mapping for key "
                                                    + key
                                                    + " is "
                                                    + mapping.get(key));
                                        }

                                    }

                                }
                                //elements(values) of maps / lists
                                else {
                                    mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
                                }
                            }
                        } catch (Exception e) {
                        }
                    }
                }
            }
        }
    }

Strtus2通过这个类来实现它特有的类型转换器配置功能,对OGNL做了扩展。OGNL在进行类型转换时会调用OgnlContetxt的getTypeConverter方法来获取类型转换器,通常情况下是Ognl自带的默认类型转换器,Struts2将自己的XWorkConverter设置为OGNL使用的类型转换器,一次来实现对OGNL类型转换的扩展。

类型转换器的查找调用顺序:

(1)检测我们是否设置了自定义类型转换器,如果找到自定义类型转换器,则使用自定义的类型转换器,否则进行下一步

(2)检查defaultTypeConverter(默认类型转换器)属性是否可用,如果可用,则使用默认类型转换器进行转换,否则进行下一步

(3)调用父类DefaultTypeConverter进行类型转换

注:这些调用顺序是覆盖的关系,即执行了(1)就不会再执行(2),比如我们自定义了int类型的类型转换器,那么不管我们是否能够实现类型转换,它都不会再去使用系统内建的类型转换器了。

内建的类型转换器

Strtus2内建的类型转换器能处理绝大多数的需求,只有在少数情况下需要我们自己自定义类型转换器。关系系统内建的类型转换器的实现可以查看XworkBasicConverter类和EnumTypeConverter类的源码。下面我们就来看看这些内建的类型转换器能够完成哪些工作。

简单类型

Struts2已经内置了基本数据类型及其包装类和其他一些常见的用于表示数字/日期类型的类型转换器,包括:

int/Integer:整数型

short/Short:短整数型

long/Long:长整型

float/Float:浮点型

double/Double:双精度型

boolean/Boolean:布尔型

byte/Byte:字节型

char/Character:字符型

BigInteger:大整数型

BigDecimal:大浮点数型

Date:日期型

ArrayList

数组和List在表单提交页面的表示方法并没有差别,只是在Action中声明时有点差别。

HelloWorld.java

public class HelloWorld extends ActionSupport {

private String[] names;

private List<String> list;

//省去set和get方法的定义

public String execute() throws Exception {

return "success";

}

}

input.jsp

<form action="hello.action" method="post">

name1 : <input type="text" name="names"/><br/>

name2 :<input type="text" name="names"><br/>

list:<input type="text" name="list[0]"><br/>

list:<input type="text" name="list[1]"><br/>

<input type="submit" value="submit"/>

</form>

数组和List表单提交页面的表示方式使用上面的两种方式均可。

Map类型

使用Map类型的方式和List相似,区别就是我们需要为Map类型指定key值,即在表单输入界面的表示形式如下:

map:<input type="text" name="map[‘name1’]"><br/>

map:<input type="text" name="map[‘name2’]"><br/>

枚举类型

枚举类型的使用方式和基本数据类型相同,只是限定了只可以输入指定的数据。

普通JaveBean

这个我们在学习Action的时候已经学习过了,就是当我们在Action中使用一个自定的类对象来接收请求参数。那么我只需要在表单提交页面做一些修改就可以让Struts2自动将请求中的参数转移到我们的JavaBean对象上了,这其实就是将复杂对象的属性分开来进行类型转换并填充。这里就不做演示了。

注:这里我们所说的数组、List、Map以及JavaBean的自动类型转换,仅仅限制在他们所包含的对象或属性都是Struts2内建拦截器能够实现类型转换的前提下。如果我们的List中的对象的类型无法使用框架内建的类型转换器来完成,那么还得需要我们自定义类型转换器才行。



分享到:
评论

相关推荐

    第十五章 Spring 类型转换(Type Conversion)1

    第十五章:Spring 类型转换小马哥 · mercyblitzSpring 类型转换Spring 类型转换的实现使用场景基于 JavaBeans 接口的类型转

    常用类型转换扩展_C#_扩展_类型转换_

    C#常用类型转换扩展 common type conversion extension

    深入浅出Struts2(附源码)

    作者处处从实战出发,在丰富的示例中直观地探讨了许多实用的技术,如数据类型转换、文件上传和下载、提高Struts 2应用的安全性、调试与性能分析、FreeMarker、Velocity、Ajax,等等。跟随作者一道深入Struts 2,聆听...

    struts2入门实例2 经典入门必备

    OGNL表达式实现类型转换 9.Struts2_03_validate_review 输入校验 ————复习前面的 10.Struts2_04_validate_method ????????????? validateXXX方法校验得到的错误信息先显示出来,然后才是...

    西门子数据类型转换_tool_数据类型转换_S7_源码

    提供了西门子PLC 不同数据类型转换Tool collection of conversion blocks for data type conversions

    struts2入门实例1

    OGNL表达式实现类型转换 9.Struts2_03_validate_review 输入校验 ————复习前面的 10.Struts2_04_validate_method ????????????? validateXXX方法校验得到的错误信息先显示出来,然后才是...

    CONVERSION(转换法).pdf

    CONVERSION(转换法).pdf

    struts2入门实例4 经典入门必备

    OGNL表达式实现类型转换 9.Struts2_03_validate_review 输入校验 ————复习前面的 10.Struts2_04_validate_method ????????????? validateXXX方法校验得到的错误信息先显示出来,然后才是...

    struts2入门实例3 经典入门必备

    OGNL表达式实现类型转换 9.Struts2_03_validate_review 输入校验 ————复习前面的 10.Struts2_04_validate_method ????????????? validateXXX方法校验得到的错误信息先显示出来,然后才是...

    基于Lua的进制间互相转换的方法, BaseConversion

    基于lua的通用进制间(二进制到三十六进制)互相转换的方法 e.g. baseConversion('29562300', 16, 2) =&gt; 00101001010101100010001100000000 baseConversion('29562300', 16, 10) =&gt; 693510912 baseConversion('...

    struts2 conversion

    NULL 博文链接:https://zw7534313.iteye.com/blog/426180

    xworkdocs 对struts2学习有帮助

    · TypeConversion Annotation · UrlValidator Annotation · Validation Annotation · Validations Annotation · VisitorFieldValidator Annotation · Building XWork · Colophon · Configuring XWork in ...

    快速2D到3D转换 Rapid 2D to 3D Conversion

    国外有关2D视频转3D视频的论文原文PDF,介绍了2D转3D的应用前景,相关方法以及最后的结果分析

    struts2总结第二章

    Struts2总结第二章 一、 局部类型转换: a) 写Date类型转换类 b) 在要进行Date类型转换的类的同一包下,新建一个properties文件 i. 名称为Date类型的数据所在的类的名称-conversion.properties ii. 内容为 name:该...

    struts2的一个不错的项目

    struts2的一个不错的项目,,时候初学者入门级别的一个项目。。struts2_3700_type_conversion.zip

    struts 2.3.4.1 最新英文版API

    org.apache.struts2.dispatcher.ng This package contains a reimagining of the traditional Struts filter dispatchers. org.apache.struts2.dispatcher.ng.filter org.apache.struts2.dispatcher.ng.listener ...

    struts自我学习过程程序以及说明

    本资源包括struts的学习程序,能够完全运行,当然,我是完全调试运行出来的,都是源代码原封上传,还有说明文档。还有自我总结资料,放到下一个文件夹当中上传,这里压缩只有这么多了,希望能去下载,那个全部是文档...

Global site tag (gtag.js) - Google Analytics