- 浏览: 17979 次
- 性别:
文章分类
最新评论
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:日期型
Array和List
数组和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中的对象的类型无法使用框架内建的类型转换器来完成,那么还得需要我们自定义类型转换器才行。
相关推荐
在本篇“Struts2学习笔记三”中,我们将聚焦于Struts2的类型转换这一核心特性。类型转换在处理用户提交的数据时非常关键,它允许Struts2自动将请求参数转换为Java对象的属性。 在Struts2中,类型转换主要由`...
在"Struts2学习笔记四(第4讲.Struts2的类型转换续)"中,我们将会深入探讨Struts2中的类型转换机制,这是一个核心特性,用于处理Action类属性与HTTP请求参数之间的数据类型转换。 在HTTP请求中,数据通常是字符串...
局部类型转换是在Action类的属性上使用注解`@TypeConversion`来实现的。这种方式只对特定的Action属性有效。例如,如果我们有一个日期类型的属性,而用户输入的是字符串,我们可以使用注解来定义如何将字符串转换为...
总结起来,Struts2是一个功能丰富的Java web开发框架,它的环境搭建、类型转换、输入校验、拦截器机制、国际化支持以及与Servlet API的整合,为开发者提供了强大且灵活的工具,帮助构建高效、可维护的Web应用。学习...
2. 如果没有找到内置转换器,Struts2会在Action类上查找注解`@ConvertProperty`或`@Conversion`,以及Action类的字段上查找注解`@ConvertType`,以获取自定义类型转换器的信息。 3. 如果注解没有提供转换器,Struts2...
类型转换(Type Conversion)是Struts2的一项重要特性,它能自动将请求参数转换为Action属性的预期类型。例如,用户输入的字符串可以自动转换为整数或日期。如果Struts2内置的转换器无法满足需求,开发者还可以...
这时,Struts2的数据类型转换器就派上了用场。它会自动尝试将接收到的字符串参数转换为目标类型的值。如果转换成功,那么Action的属性就会被正确赋值;如果转换失败,Struts2会抛出一个异常,并可以自定义错误消息...
Struts2是一个流行的Java web开发框架,其核心是Action,而类型转换是Struts2处理用户输入数据的一个重要环节。Struts2内置了多种类型转换,可以将HTTP请求中的字符串数据转换为各种基本类型和复杂类型,如日期、...
这就是"struts2自定义类型转换器"的主题。 首先,理解Struts2的类型转换机制。当Struts2接收到HTTP请求时,它会尝试将请求参数与Action类的属性进行绑定。这个过程涉及到了类型转换,Struts2内置了一套转换器机制,...
在Struts2框架中,自定义类型转换器是开发者为了满足特定需求,对框架默认的类型转换机制进行扩展的一种方式。Struts2允许我们创建自己的转换器类来处理输入数据,确保模型对象的属性能够正确地被转换为预期的数据...
### Struts2学习笔记 #### 一、Struts2配置文件详解 在深入理解Struts2框架之前,我们首先需要了解其核心配置文件——`struts.xml`。 **1. struts.xml配置** ```xml <!DOCTYPE struts PUBLIC "-//Apache ...
Struts2是一个强大的MVC框架,它在处理Web应用程序时提供了许多便利,其中包括类型转换功能。类型转换是将用户输入的数据自动转化为服务器端处理所需的类型,这样可以避免手动转换带来的繁琐工作。本篇文章将深入...
Struts2是一个强大的MVC框架,它在处理用户请求时提供了丰富的功能,其中包括类型转换(Converter)。类型转换是Struts2框架中一个重要的特性,它允许我们把前端表单提交的数据自动转换为后端Java对象的属性。在这个...
首先,我们来看看**类型转换(Type Conversion)**。在Struts2中,用户通过表单提交的数据通常是字符串,而服务器端处理时往往需要将其转化为其他类型,如整型、浮点型或日期等。Struts2内置了一套类型转换机制,...
在Struts2中,类型转换是一项关键功能,它允许框架将用户输入的数据自动转换为应用程序所需的类型。这使得开发人员可以更加专注于业务逻辑,而不用过于担心数据类型匹配的问题。 类型转换在Struts2中的主要作用是...
自定义类型转换器需要实现`org.apache.struts2.util.TypeConverter`接口,或者继承`org.apache.struts2.convention.converters.DefaultConversionProvider`基类,覆盖`convertValue()`方法。然后,通过在Action类或...
### Struts2自定义类型转换:深入解析与实践 #### 引言 在Web开发中,数据类型转换是一项常见的需求,特别是在用户输入的数据与后端处理的数据类型不一致时。Struts2框架提供了强大的类型转换机制,允许开发者...
在Struts2中,类型转换和国际化是两个重要的特性,用于处理数据类型之间的转换以及多语言环境下的内容显示。 **类型转换**是Struts2处理请求参数与Action类属性之间数据类型不匹配的过程。当用户通过表单提交请求时...