
web层框架有一个很重要的功能就是接收从页面端提交过来的数据封装到JavaBean中,或者说会提供一种接收页面端提交的数据的机制。struts2作为一个web层框架当然也不例外,只是说各个web框架所提供的这种机制实现是不一样的,当然你不使用框架提供的数据接收机制而用Servlet中request.getParameter(name);
也是可以的。
在struts2中我们接收页面端参数一般是通过在Action
中写相应的属性,其本质是将页面端提交的数据设置到ValueStack
中,Action
也是位于ValueStack
中的。在进行参数设置之前,我们有可能要进行一定的类型转换,因为从页面端提交过来的数据都是字符串型,而在Java世界中数据类型有很多,这样就出现了页面中的数据结构与Java世界的数据结构不一致的情况,所以我们需要一种能在这两者之间进行转换的工具以解决这种不匹配的矛盾,在struts2中就表现为转换器类(TypeConverter
).这种转换的实现是通过OGNL实现的,在OGNL中定义了一个ognl.TypeConverter
接口:
public interface TypeConverter {
public Object convertValue(Map context,Object target,Member member,String propertyName,Object value,Class toType);
}
复制
要想让OGNL完成类型转换工作就必须向OGNL提供一个该接口的实现类,在OGNL中该接口的默认实现类是ognl.DefaultTypeConverter
但在XWork中,并没有直接使用ognl.DefaultTypeConverter
实现类,甚至都没有使用ognl.TypeConverter
接口,而是XWork自己定义了一个与ognl.TypeConverter
接口一样的com.opensymphony.xwork2.conversion.TypeConverter
接口,其名称和接口函数定义完全相同。XWork自行定义TypeConverter
接口的目的在于对外屏蔽类型转换的实现细节,从而能够将XWork对TypeConverter
的扩展实现纳入到XWork的容器中进行管理,从而方便对OGNL原始的TypeConverter
接口进行扩展并支持更加广泛的类型转换逻辑。XWork对类型转换方式TypeConverter
的默认实现类是XWorkConverter
,并使用了装饰模式,将其接口的实现封闭在OgnlTypeConverterWrapper
内部。这样一来就可以完成利用装饰模式优点,不仅屏蔽原始的ognl.TypeConverter
默认实现,并且有利于XWork自身在装饰类中插入框架所提供的用户扩展功能。下面是OgnlTypeConverterWrapper
的源代码:
public class OgnlTypeConverterWrapper implements ognl.TypeConverter {
private TypeConverter typeConverter;
public OgnlTypeConverterWrapper(TypeConverter conv) {
if (conv ==null) {
throw new IllegalArgumentException("Wrapped type converter cannot be null");
}
this.typeConverter =conv;
}
public Object convertValue(Map context,Object target,Member member,
String propertyName,Object value,Class toType) {
return typeConverter.convertValue(context,target,member,propertyName,value,toType);
}
public TypeConverter getTarget() {
return typeConverter;
}
}
复制
OgnlTypeConverterWrapper
实现了ognl.TypeConverter
接口,这个类就可以交给OGNL进行类型转换,因为这里使用了装饰模式,所以真正做类型转换工作的是其内部的typeConverter
变量,该变量在运行时就是一个XWorkConverter
对象,所以才说XWorkConverter
是XWork中TypeConverter
的默认实现。
下面的setRoot
方法是创建ValueStack
后设置root
对象时调用的方法,在该方法进创建了OgnlContext
对象,并把xworkConverter
包装起来:
protected void setRoot(XWorkConverter xworkConverter,
CompoundRootAccessor accessor,CompoundRoot compoundRoot,boolean allowStaticMethodAccess) {
this.root =compoundRoot;
this.securityMemberAccess =new SecurityMemberAccess(allowStaticMethodAccess);
//创建OgnlContext对象,并且把xworkConverter包装在OgnlTypeConverterWrapper内部
this.context =Ognl.createDefaultContext(this.root,accessor,new OgnlTypeConverterWrapper(xworkConverter),
securityMemberAccess);
context.put(VALUE_STACK,this);
Ognl.setClassResolver(context,accessor);
((OgnlContext) context).setTraceEvaluations(false);
((OgnlContext) context).setKeepLastEvaluation(false);
}
复制
从源代码中我们可以看到,被OgnlTypeConverterWrapper
所装饰的实际实现类已经是XWork框架中的TypeConverter
,而这个TypeConverter
接口也就成为我们进行自由扩展自定义类型转换方式的容器。在XWork中TypeConverter
接口的直接实现类是com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter
,它实现了最为基本的类型转换方式,如基本数据类型转换,枚举类型的转换,这个类中的逻辑很简单就不贴源码了。
到这里我们应该会有一个疑问,如果自定义TypeConverter
从而能够将其插入到XWork框架之中并被XWork所识别呢,我们先看一下XWork中TypeConverter
的实现结构图。
从这个结构图中可以看到对于直接实现的DefaultTypeConverter
使用了两个完全不同的扩展方式。首先,使用“继承”的方式对DefaultTypeConverter
进行基本的扩展(XWorkBasicConterter
),覆盖其默认行为方式,然后进一步使用装饰模式将这个被扩展的XWorkBasicConverter
封装在内部,对外呈现出XWorkConverter
这个装饰类。
XWorkBasicConterter
提供了比DefaultTypeConverter
更为广泛的类型转换支持,除了基本数据类型外还包含了Date
类型,Collection
类型等。
在XWorkConverter
中,使用了装饰模式,实现了我们期待的“自定义类型转换”的注册与使用。
在XWorkConverter
的converterValue()
该方法中,会首先通过getConverter
方法的调用来获取当前Java类所对应的类型转换处理器,即查找类级别的转换器与全局转换器,如果查找不到的话再使用缺省的转换器进行转换,这个缺省的转换器就是一个XWorkBasicConterter
对象,在实现化XWorkConverter
时通过struts2容器注入进来。通过这样一种机制就实现了自定义转换器的扩展。
到这里虽然知道了struts2是如何使用转换器把页面提交的字符数据转换成我们想要的Java类型的数据,但是在struts2中,类型转换到底发生在什么时候。有一点知道的是struts2在接收页面提交数据是将数据设置到了ValueStack
中,即调用的是ValueStack.setValue()
方法,肯定是在此方法中先进提交的数据进行了相应的转换,而调用相应的setter方法进行赋值的,为此我故意让struts2类型转换失败让其打印出堆栈信息,如下图:
最终调用了ognl.OgnlRuntime.callAppropriateMethod
方法:
public static Object callAppropriateMethod(OgnlContext context,Object source,Object target,String methodName,String propertyName,List methods,Object[] args) throws MethodFailedException
{
//省略了很多代码...
//先获取相应的方法,设置提交的参数时就是要获取相应的setter方法
Method method =getAppropriateMethod(context,source,target,propertyName,methods,args,actualArgs);
//省略了很多代码...
//调用获取到的方法
return invokeMethod(target,method,convertedArgs);
//省略了很多代码...
}
复制
现在关键的就是getAppropriateMethod
方法,在该方法中又调用了getConvertedType
方法,下面该方法源码:
public static Object getConvertedType(OgnlContext context,Object target,Member member,String propertyName,Object value,Class type) {
return context.getTypeConverter().convertValue(context,target,member,propertyName,value,type);
}
复制
到这里大家就应该明白了,其实就是在获取OgnlContext
中的TypeConverter
对象,即OgnlTypeConverterWrapper
对象,使用该对象进行类型转换。
--------------- END ---------------
每一篇文章都是小编精心为您准备的,写文不易,费时费脑;『分享』+『点赞』+『在看』三连击是小编坚持的最大动力,拜谢!